4

目前我尝试编写一个函数retrieveKeys(),它给我 a 的键std::map并将其存储在一些std::container. 该函数应以两种方式通用:

  • 接受std::mapstd::unordered_map作为参数类型。
  • 返回用户定义容器中的键,例如std::vectorstd::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);
                          ^~~~~~~~~~~~
*/

使用 clang 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);
                              ^~~~~~~~~~~~
    */
}
4

4 回答 4

4
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);
}

演示

于 2015-09-17T19:14:29.230 回答
3

不,编译器无法判断返回类型应该是什么,因为它没有可用于确定的信息(您可以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);
于 2015-09-17T18:53:40.660 回答
1

不,调用站点没有返回类型推导,因为编译器将缺少必要的上下文。与例如比较std::make_unique

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

一般来说,模板参数推导适用于提供的模板参数。提供一个外参数将推导出一切

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
}
于 2015-09-17T19:04:07.893 回答
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;
}
于 2015-09-17T19:28:17.473 回答