c++templatesreturn-type-deduction

Deduce template return type in C++


Currently I try to write a function retrieveKeys() which gives me the keys of a std::map and stores it in some std::container. The function shall be generic in two ways:

Currently the use of this function works as follows:

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

With the following function:

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

It would be nice if I would not have to write the return type explicitly. But when I try something like

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

I get the mentioned error when compiling with clang 3.6 (C++17).

So my question is: Is it possible to rewrite the function so that the return type can be reduced by the compiler?

Here again the complete code for easy copying:

#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);
                              ^~~~~~~~~~~~
    */
}

Solution

  • 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