推导 C++ 中的模板返回类型

Mil*_*nia 5 c++ templates return-type-deduction

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

  • 接受std::mapstd::unordered_map作为参数类型。
  • 返回用户定义的容器中的键,例如std::vectoror 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);
Run Code Online (Sandbox Code Playgroud)

具有以下功能:

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;
}
Run Code Online (Sandbox Code Playgroud)

如果我不必显式地编写返回类型,那就太好了。但是当我尝试类似的事情时

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

使用 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);
                              ^~~~~~~~~~~~
    */
}
Run Code Online (Sandbox Code Playgroud)

Pio*_*cki 8

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);
}
Run Code Online (Sandbox Code Playgroud)

演示版

  • @TemplateRex好吧,信息必须来自*某个地方*...要么必须知道目标类型,要么必须使用显式模板参数。 (2认同)
  • @Milania 该引用限定符确保仅当 `ReturnTypeDeducer` 是右值时才使用转换运算符(立即由调用 `retrieveKeys` 产生),以降低从悬空引用进行转换的风险。考虑以下代码:“auto foo =retrieveKeys(std::map&lt;int,int&gt;{});”。现在,“foo”存储了一个悬空引用,但是由于这个引用限定符,您不会像“std::vector&lt;int&gt; v = foo;”那样意外地使用它 (2认同)