5

I'm implementing some template-based serialization. I implemented the templated function for std::map, but now I'm using an std::unordered_map. I would rather not copy & paste the entire function and just change the parameter type. Is there any way to make a template which takes only a map or an unordered map?

Claudiu
  • 224,032
  • 165
  • 485
  • 680
  • Why such a restriction? What if someone wants to use your function with their own implementation of a map that has the same interface as `std::map`? – Brian Bi Sep 09 '14 at 16:49
  • @Brian: That would be fine, but I think I am restricted by the serialization framework from defining a `serialize` function that takes any parameter. Wouldn't it clash with other `serialize` functions that say, take ints specifically? – Claudiu Sep 09 '14 at 16:51

5 Answers5

5
template <typename MAP>
void generic_foo(MAP& map)
{
    // generic implementation of your function
    // that works with unordered_map and map

    using K = typename MAP::key_type;
    using T = typename MAP::mapped_type;
}

// matches any possible implementation of std::unorderd_map
template <class Key,                                    
          class T,                                   
          class Hash,                       
          class Pred,                  
          class Alloc>
void foo(std::unordered_map<Key, T, Hash, Pred, Alloc>& m)
{
    // signature matched! forward to your implementation
    generic_foo(m);
}

// matches any possible implementation of std::map        
template <class Key,                                    
          class T,                          
          class Compare,                  
          class Alloc>
void foo(std::map<Key, T, Compare, Alloc>& m)
{
    // signature matched! forward to your implementation
    generic_foo(m);
} 

LIVE DEMO

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
  • But this is not what Paul Evans answered? – Amadeus Sep 09 '14 at 17:09
  • 1
    @TomásBadan It is, but it's got a fully-functional example. It's a more complete answer. – linguamachina Sep 09 '14 at 17:16
  • One issue: how can I access the key & value types from `generic_foo`? I need to get `pair`s out of the map – Claudiu Sep 09 '14 at 17:24
  • @PiotrS.: I mean the types, not the values. I can't do `std::pair item;` since those types aren't in the template. I tried `MAP::value_type item;` but it says "need ‘typename’ before ‘Map:: value_type’ because ‘Map’ is a dependent scope" – Claudiu Sep 09 '14 at 17:27
  • @Claudiu Then add `typename`...`typename MAP::value_type item;` – T.C. Sep 09 '14 at 17:28
  • @PiotrS.: Oh nice, didn't realize it was that simple ([this](http://stackoverflow.com/questions/3311633/nested-templates-with-dependent-scope) helped explain it). Thanks for all the help! – Claudiu Sep 09 '14 at 17:32
2

Simply overload the function as a non-templated function with one overload to take an std::map and another to take an std::unordered_map. Have these two functions call a hidden template that takes anything but can only be called by them. One way to do this is to hide it in an anonymous namespace.

Paul Evans
  • 27,315
  • 3
  • 37
  • 54
  • 3
    @PiotrS. This **doesn't** *copy & paste the entire function*! That is the function that is renamed, hidden and simply changed to take anything rather than use an `std::map` – Paul Evans Sep 09 '14 at 16:52
  • *non-templated function that takes std::map* makes no use, since the types are hard-coded, whereas the OP's original function is a template. any mismatch with a type within map/unorderd_map will resolve the overload to the generic one – Piotr Skotnicki Sep 09 '14 at 17:24
  • This was the right idea, and I upvoted, but Piotr helped take me to the final working implementation – Claudiu Sep 09 '14 at 17:33
2

With C++20 concepts

To support specifically std::map and std::unordered_map:

template<typename T> struct is_unordered_map
   : public std::false_type {};

template<typename... Args>
struct is_unordered_map<std::unordered_map<Args...>>
   : public std::true_type {};

template<typename T> struct is_map
   : public std::false_type {};

template<typename... Args>
struct is_map<std::map<Args...>>
   : public std::true_type {};

template<typename C>
concept UnorderedMap = is_unordered_map<C>::value;

template<typename C>
concept Map = is_map<C>::value;

template<typename C>
concept MappingContainer =
    Map<C> || UnorderedMap<C>;

void foo(const MappingContainer auto& m) { ... }

Code: https://godbolt.org/z/MrsaGn


To support any type that behaves as a mapping type:

template<typename C>
concept MappingContainer = requires(C c) {
    typename C::key_type;
    typename C::mapped_type;
    typename C::value_type;
    typename C::iterator;
    requires std::same_as<decltype(c.begin()), typename C::iterator>;
    requires std::same_as<decltype(c.end()), typename C::iterator>;
    requires std::same_as<
        typename C::value_type,
        std::iter_value_t<typename C::iterator>
    >;
    requires std::same_as<
        typename C::value_type,
        std::pair<const typename C::key_type, typename C::mapped_type>
    >;
};

void foo(const MappingContainer auto& m) { ... }

Code: https://godbolt.org/z/jPfMhT

Amir Kirsh
  • 12,564
  • 41
  • 74
1
#include<type_traits>
template<typename T>
void foo(T t){
    static_assert(std::is_same<T, std::map</*some_type*/>::value
               || std::is_same<T, std::unordered_map</*some_type*/>::value,
                  "Foo can only get std::map or std::unordered_map.");
}
GingerPlusPlus
  • 5,336
  • 1
  • 29
  • 52
0

Here is an example that extracts keys/values from std::map or std::unordered_map

template <typename M>
std::unordered_set<typename M::key_type> GetMapKeys(const M& a_map) {
  std::unordered_set<typename M::key_type> keys;
  for (auto const& e : a_map) {
    keys.emplace(e.first);
  }
  return keys;
}

template <typename M>
std::vector<typename M::mapped_type> GetMapValues(const M& a_map) {
  std::vector<typename M::mapped_type> values;
  values.reserve(a_map.size());
  for (auto const& e : a_map) {
    values.push_back(e.second);
  }
  return values;
}
Curious
  • 2,783
  • 3
  • 29
  • 45