6

I'm trying to create a function that gets the keys from a std::map or an std::unordered_map. I could use a simple overload, but first I'd love to know what's wrong with this code.

template<typename K, typename V, template<typename, typename> class TContainer>  
std::vector<K> getKeys(const TContainer<K, V>& mMap)
{
    std::vector<K> result;
    for(const auto& itr(std::begin(mMap)); itr != std::end(mMap); ++itr) result.push_back(itr->first);
    return result;
}

When calling it with an std::unordered_map, even specifying all template typenames manually, clang++ 3.4 says:

template template argument has different template parameters than its corresponding template template parameter.

WhozCraig
  • 65,258
  • 11
  • 75
  • 141
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416

1 Answers1

11

The problem is that std::map and std::unordered_map are not in fact templates with two parameters. They are:

namespace std {
    template <class Key, class T, class Compare = less<Key>,
              class Allocator = allocator<pair<const Key, T>>>
    class map;

    template <class Key, class T, class Hash = hash<Key>,
              class Pred = equal_to<Key>,
              class Allocator = allocator<pair<const Key, T>>>
    class unordered_map;
}

Here's something similar that does work:

template <typename K, typename... TArgs, template<typename...> class TContainer>
std::vector<K> getKeys(const TContainer<K, TArgs...>& mMap)
{
    std::vector<K> result;
    for (auto&& p : mMap)
        result.push_back(p.first);
    return result;
}

The version I would prefer:

template <typename Container>
auto getKeys2(const Container& mMap) -> std::vector<typename Container::key_type>
{
    std::vector<typename Container::key_type> result;
    for (auto&& p : mMap)
        result.push_back(p.first);
    return result;
}

A demo program using both functions: http://ideone.com/PCkcu6

aschepler
  • 70,891
  • 9
  • 107
  • 161
  • Oh, I feel so stupid now. Is there a way to match both `map` and `unordered_map` without creating two different overloads? The number of template parameters differs. – Vittorio Romeo Aug 13 '13 at 23:17
  • 1
    Edited with a possible "fix". But it would probably be better to somehow use `decltype(mMap)::key_type`. – aschepler Aug 13 '13 at 23:29
  • Came up with the same fix minutes before you posted it, hehe. Thanks for everything! – Vittorio Romeo Aug 13 '13 at 23:30
  • 1
    Great improvement in my opinion. I changed the `auto` to `const auto&` and implemented it in my library. – Vittorio Romeo Aug 13 '13 at 23:44
  • This answer works perfectly. At Bloomberg I must have found another solution but don't remember it. The problem stems from wanting underneath to actually store the map with a decorated mapped_type so not exactly the one the user provides. – CashCow Dec 30 '22 at 07:20