Search code examples
c++compilationstlmetaprogrammingsfinae

How can I use SFINAE to distinguish MyMap, std::map and std::unordered_map?


I want to make a struct that can use any implementation of maps. Let's call it a MapHolder. Currently, it looks like this:

template<template<class, class, class...> class MapType>
struct MapHolder
{
  MapType<int, int> m_map;
}

And that would work with any map.

But I want to use custom comparator/allocator/hash when possible as well:

 // Works only with std::unordered_map
 MapType<int, int, my::hash<int>> m_map;

Or

 // Works only with std::map
 MapType<int, int, std::greater<>> m_map;

And it will compile only with one of the classes.

Can't SFINAE help me to make compiler choose the most appropriate declaration (i.e. first for MapHolder<MyMap>, second for MapHolder<std::unordered_map> and so on)?


Solution

  • I would use partial template specialization here. Something as:

    template<template<class, class, class...> class MapType>
    struct MapHolder
    {
    private:
       template <typename> struct ThirdArgument;
    
       template <typename K,  typename V, typename C, typename A>
       struct ThirdArgument<std::map<K, V, C, A>>
       {
          using type = std::greater<K>;
       };
    
       template <typename K,  typename V, typename H, typename E, typename A>
       struct ThirdArgument<std::unordered_map<K, V, H, E, A>>
       {
          using type = std::hash<K>;
       };
    
    public:
       MapType<int, int,
          typename ThirdArgument<MapType<int, int>>::type
       > m_map;
    };
    
    int main()
    {
        MapHolder<std::map> m1;
        MapHolder<std::unordered_map> m2;
    }
    

    Live demo: https://godbolt.org/z/mEaFZv.