2015-09-17 33 views
3

當前我嘗試編寫一個函數retrieveKeys(),它給了我一個std :: map的鍵並將其存儲在某個std :: container中。該功能在兩種方式中應該是通用的:在C++中推導模板返回類型

  • 接受std :: map和std :: unordered_map作爲參數類型。
  • 返回用戶定義的容器中的鍵, G。 std :: vector或std :: deque(容器必須支持push_back()方法)。

目前使用該功能的工作原理如下:

std::unordered_map<int, int> testMap; 
std::map<int, int> testMap2; 

std::vector<int> keys1 = retrieveKeys<std::vector>(testMap);  
std::deque<int> keys2 = retrieveKeys<std::deque>(testMap); 
std::vector<int> keys3 = retrieveKeys<std::vector>(testMap2); 
std::deque<int> keys4 = retrieveKeys<std::deque>(testMap2); 

用下面的函數:

template<template<typename...> typename KeyContainer, typename... KeyContainer_Rest, 
     template<typename...> typename MapContainer, typename K, typename V, typename... MapContainer_Rest> 
inline KeyContainer<K, KeyContainer_Rest...> retrieveKeys(const MapContainer<K, V, MapContainer_Rest...>& map) 
{ 
    KeyContainer<K, KeyContainer_Rest...> keys; 

    for (const auto& m : map) 
    { 
     keys.push_back(m.first); 
    } 

    return keys; 
} 

這將是很好,如果我也不會寫的返回類型明確。但是當我嘗試類似的東西時

std::vector<int> keys1_ = retrieveKeys(testMap); 
/* 
error: no matching function for call to 'retrieveKeys' 
std::vector<int> keys1_ = retrieveKeys(testMap); 
          ^~~~~~~~~~~~ 
*/ 

我在使用叮噹聲3.6(C++ 17)編譯時遇到上述錯誤。

所以我的問題是:是否有可能重寫函數,以便編譯器可以減少返回類型?

這裏又容易複製的完整代碼:

#include <deque> 
#include <vector> 
#include <unordered_map> 
#include <map> 

template<template<typename...> typename KeyContainer, typename... KeyContainer_Rest, 
     template<typename...> typename MapContainer, typename K, typename V, typename... MapContainer_Rest> 
inline KeyContainer<K, KeyContainer_Rest...> retrieveKeys(const MapContainer<K, V, MapContainer_Rest...>& map) 
{ 
    KeyContainer<K, KeyContainer_Rest...> keys; 

    for (const auto& m : map) 
    { 
     keys.push_back(m.first); 
    } 

    return keys; 
} 

int main() 
{ 
    std::unordered_map<int, int> testMap; 
    std::map<int, int> testMap2; 

    std::vector<int> keys1 = retrieveKeys<std::vector>(testMap);  
    std::deque<int> keys2 = retrieveKeys<std::deque>(testMap); 
    std::vector<int> keys3 = retrieveKeys<std::vector>(testMap2); 
    std::deque<int> keys4 = retrieveKeys<std::deque>(testMap2); 

    //std::vector<int> keys1_ = retrieveKeys(testMap); 
    /* 
    error: no matching function for call to 'retrieveKeys' 
    std::vector<int> keys1_ = retrieveKeys(testMap); 
           ^~~~~~~~~~~~ 
    */ 
} 

回答

2
template <typename K, typename M> 
struct ReturnTypeDeducer 
{ 
    const M& map; 

    ReturnTypeDeducer(const M& m) : map(m) {} 

    template <template <typename...> typename KeyContainer, typename... KeyContainer_Rest> 
    operator KeyContainer<K, KeyContainer_Rest...>() && 
    { 
     KeyContainer<K, KeyContainer_Rest...> keys; 
     for (const auto& m : map) 
     { 
      keys.push_back(m.first); 
     } 
     return keys; 
    } 
}; 

template <template <typename...> typename MapContainer, typename K, typename V, typename... MapContainer_Rest> 
inline ReturnTypeDeducer<K, MapContainer<K, V, MapContainer_Rest...>> retrieveKeys(const MapContainer<K, V, MapContainer_Rest...>& map) 
{ 
    return map; 
} 

int main() 
{ 
    std::unordered_map<int, int> testMap; 
    std::map<int, int> testMap2; 

    std::vector<int> keys1 = retrieveKeys(testMap);  
    std::deque<int> keys2 = retrieveKeys(testMap); 
    std::vector<int> keys3 = retrieveKeys(testMap2); 
    std::deque<int> keys4 = retrieveKeys(testMap2); 
} 

DEMO

+0

的'返回類型使用'auto'但是這並不'auto'在調用點工作,而是依靠隱式轉換 – TemplateRex

+0

@TemplateRex那麼,信息必須來自某處* ...必須知道目標類型,或者必須使用顯式模板參數。 –

+0

@ T.C。當然,但這仍然是巧妙的,我認爲鼓勵隱式轉換是一種糟糕的風格。 – TemplateRex

2

號有沒有辦法讓編譯器告訴返回類型應該是什麼,因爲它有沒有信息可以用它來確定(你可以在立即將它分配給所需類型的變量的上下文之外調用retrieveKeys())。

但是,您可以通過使用auto減少重複代碼:

auto keys1 = retrieveKeys<std::vector>(testMap);  
auto keys2 = retrieveKeys<std::deque>(testMap); 
auto keys3 = retrieveKeys<std::vector>(testMap2); 
auto keys4 = retrieveKeys<std::deque>(testMap2); 
+3

並在retrieveKeys' – TemplateRex

1

沒有,是在調用點沒有返回類型推演,因爲編譯器會缺乏必要的環境。與例如std::make_unique

auto derived_ptr = std::make_unique<Derived>(args); // have to specify return type 

一般來說,模板參數扣作品上,那麼,所提供的模板參數。供給out參數將演繹一切

template<class InputParam, class OutputParam> 
void copy(InputParam const& src, OutParam& dst) { /* bla */ } 

// call as: 
InputParam src = /* fill */; 
OutputParam dst; // empty 
copy(src, dst) // template arguments deduced from supplied src, dst 

相反,如果沒有它,你必須明確地提供模板參數:

template<class InputParam, class OutputParam> 
OutputParam copy(InputParam const& src) { OutputParam x; /* bla */ return x; } 

// call as: 
InputParam src = /* fill */; 
auto dst = copy<OutputParam>(src); // InputParam deduced from src, supply OutputParam template argument 

與此相反,在點定義的,C + +14確實有返回類型推演,所以你可以寫

template<template<typename...> typename KeyContainer, typename... KeyContainer_Rest, 
     template<typename...> typename MapContainer, typename K, typename V, typename... MapContainer_Rest> 
auto // <-- here 
retrieveKeys(const MapContainer<K, V, MapContainer_Rest...>& map) 
{ 
    KeyContainer<K, KeyContainer_Rest...> keys; 

    for (const auto& m : map) 
    { 
     keys.push_back(m.first); 
    } 

    return keys; // compiler will deduce return-type of retrieveKeys from this 
} 
0
An example of how you can use container::key_type 

template <class CONTAINER> 
std::vector <typename CONTAINER::key_type> 
retrieveKeys(CONTAINER container) 
{ 
    std::vector <typename CONTAINER::key_type> keys; 
    for (auto itr : container) 
    { 
     keys.push_back(itr.first); 
    } 
    return keys; 
} 


int _tmain(int argc, _TCHAR* argv[]) 
{ 
    typedef std::map <int, int> MYMAP; 
    MYMAP values; 
    values.insert(std::make_pair(1, 1)); 
    values.insert(std::make_pair(2, 2)); 
    values.insert(std::make_pair(3, 3)); 

    typedef std::vector <typename MYMAP::key_type> KEYVECTOR; 
    KEYVECTOR keys = retrieveKeys<MYMAP>(values); 
    for (auto itr : keys) 
     std::cout << itr std::endl; 
}