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?
Asked
Active
Viewed 1,513 times
5

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

Piotr Skotnicki
- 46,953
- 7
- 118
- 160
-
-
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 -
-
@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) { ... }

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