c++templatestemplate-templates

How to provide std::map as an argument to a template template parameter


I have a class that needs to use some sort of map. By default, I want to use std::map, but I also want to give the user the ability to use something different if they want (e.g. std::unordered_map or maybe even a user created one).

So I have code that looks like

#include <map>

template<class Key, template<class, class> class Map = std::map>
class MyClass {
};

int main() {
  MyClass<int> mc;
}

But then, g++ complains

test.cpp:3:61: error: template template argument has different template parameters than its corresponding template template parameter
template<class Key, template<class, class> class Map = std::map>
                                                            ^
test.cpp:8:14: note: while checking a default template argument used here
  MyClass<int> mc;
  ~~~~~~~~~~~^
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1/map:781:1: note: too many template parameters in template template argument
template <class _Key, class _Tp, class _Compare = less<_Key>,
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test.cpp:3:21: note: previous template template parameter is here
template<class Key, template<class, class> class Map = std::map>
                    ^~~~~~~~~~~~~~~~~~~~~~
1 error generated.

So it looks like g++ is unhappy that std::map has default arguments.

Is there a way I can allow Map to be any sort of template that can accept at least two template arguments?

I would prefer to stick with C++98 if I can, but I'm open to C++11.


Solution

  • The problem is that your template template parameter has only two template parameters, as opposed to map, which has four.

    template<class Key, template<class, class, class, class> class Map = std::map>
    class MyClass {
    };
    

    Or

    template<class Key, template<class...> class Map = std::map>
    class MyClass {
    };
    

    Should compile.
    However, to avoid such problems, try to take the map type instead, and extract the key type via the corresponding member typedef. E.g.

    template <class Map>
    class MyClass {
        using key_type = typename Map::key_type;
    };