How to write a C++ concept restricting the template to std::map and std::unordered_map

1.3k views Asked by At

If I have a templates looking like this that perform a simple operation of copying for the sake of example but for std::map and std::unordered_map:

template<typename T1, typename T2>
inline std::map<T1, T2> map_copy(std::map<T1,T2> const& a) {
  std::map<T1,T2> output;
  output = a;
  return output;
}

template<typename T1, typename T2>
inline std::unordered_map<T1, T2> map_copy(std::unordered_map<T1,T2> const& a) {
  std::unordered_map<T1,T2> output;
  output = a;
  return output;
}

Is there a way, probably using C++ concepts, to reduce these definitions to just one, constraining the possible types just to std::map and std::unordered_map?

3

There are 3 answers

3
Artyer On BEST ANSWER

You can use this concept to check if a type is a std::map or std::unordered_map:

template<typename T>
concept map_type = 
    std::same_as<T, std::map<typename T::key_type, typename T::mapped_type, typename T::key_compare, typename T::allocator_type>> ||
    std::same_as<T, std::unordered_map<typename T::key_type, typename T::mapped_type, typename T::hasher, typename T::key_equal, typename T::allocator_type>>;
    
template<map_type T>
T map_copy(T const& a) {
    T output;
    output = a;
    return output;
}

For generic checking if T is an instantiation of template<typename...> struct Template;, you could do something like:

template<template<typename...> class Template, typename Class>
struct is_instantiation : std::false_type {};

template<template<typename...> class Template, typename... Args>
struct is_instantiation<Template, Template<Args...>> : std::true_type {};

template<typename Class, template<typename...> class Template>
concept is_instantiation_of = is_instantiation<Template, Class>::value;

template<typename T>
concept map_type =
    is_instantiation_of<T, std::map> || is_instantiation_of<T, std::unordered_map>;

template<map_type T>
T map_copy(T const& a) {
    T output;
    output = a;
    return output;
}

template<is_instantiation_of<std::map> T>
T ordered_map_copy(T const& a) {
    return a;
}
5
leslie.yao On

You can add a constraint,

template<template <typename...> class MAP, typename T1, typename T2>
inline MAP<T1, T2> map_copy(MAP<T1,T2> const& a) 
  requires std::is_same_v<MAP<T1,T2>, std::map<T1,T2>> || 
    std::is_same_v<MAP<T1,T2>, std::unordered_map<T1,T2>> {
  MAP<T1,T2> output;
  output = a;
  return output;
}

Or define a concept,

template<template <typename...> class MAP, typename T1, typename T2>
concept Mappable = std::is_same_v<MAP<T1,T2>, std::map<T1,T2>> ||
  std::is_same_v<MAP<T1,T2>, std::unordered_map<T1,T2>>;

then

template <template <typename...> class MAP, typename T1, typename T2>
inline MAP<T1, T2> map_copy(MAP<T1,T2> const& a) requires Mappable<MAP, T1, T2> {
  MAP<T1,T2> output;
  output = a;
  return output;
}
0
Малюта Скуратов On

Also you can use this conception:

template <typename Container>
concept Associative = requires(Container cont) {
    typename Container::key_type;
    typename Container::mapped_type;
    { cont.begin() } -> std::same_as<typename Container::iterator>;
    { cont.end() } -> std::same_as<typename Container::iterator>;
};

template<Associative Container>
void func(const Container& cont) {
  // ...
}