9

There may be many case in which we want to perform some operation on a std::map or a std::unordered_map that is exactly the same, independently from the type of the map. Let us consider the following example:

#include <map>
#include <unordered_map>
#include <iostream>

template< template <typename,typename> class Container >
void printMap(Container<int, long> inputMap, bool additionalParam = false)
{
    for (const pair<int,long> p : inputMap)
        cout<<p.first <<","<< p.second <<std::endl;
}

int main()
{
int a = 1;
long b = 2;
map<int,long> map1;
map1.emplace(a,b);
unordered_map<int,long> map2;
map2.emplace(a,b);
printMap(map1);
printMap(map2);

return EXIT_SUCCESS;
}

If I try to compile the example above, I have this:

error: no matching function for call to ‘printMap(std::map<int, long int>&)’

I read about the use of template of template in this post. What is the right way to do that?

einpoklum
  • 118,144
  • 57
  • 340
  • 684
Andrea Araldo
  • 1,332
  • 14
  • 20
  • A [`std::map`](http://en.cppreference.com/w/cpp/container/map) has more than two template parameters. You also have a type `Compare` and a type `Allocator`. You have to specify all, even the defaulted ones. – Henri Menke Aug 09 '17 at 21:56
  • Note that your code will just work come C++17; the solutions below are all for C++14 and below. – Daniel H Aug 09 '17 at 22:08

3 Answers3

5

Try with

template< template <typename...> class Container, typename ... Ts >
void printMap(Container<int, long, Ts...> inputMap,
              bool additionalParam = false)

The (bigger) problem in your code is that std::map and std::unordered_map are template classes with four (not two) template parameters. The 3rd and the 4th have default value so you can define a std::map object as

 std::map<int, long> map1;

but, with default parameter, you're defining it as

 std::map<int, long, std::less<int>,
          std::allocator<std::pair<const int, long> >> map1;

(ps: or you can make it simple and use auto, as in the Semyon Burov's solution; +1)

max66
  • 65,235
  • 10
  • 71
  • 111
5

Compiler cant deduce template argument if you define it that way. Try to use:

template<typename Map>
void printMap(const Map& map, bool additionalParam = false) {
    for (const auto& p : map)
        cout<<p.first <<","<< p.second <<std::endl;
}

If you need to check, that Map is exactly Map<int, long int>, then add static assertion to a body of function:

static_assert( std::is_same< typename Map::key_type, int >::value &&
                       std::is_same< typename Map::mapped_type, long >::value, "!");
Semyon Burov
  • 1,090
  • 1
  • 9
  • 15
  • I added a `static_assert` to maintain the type constraints. – Henri Menke Aug 09 '17 at 22:00
  • @HenriMenke Personally, I dont see any problem in ability to print any kind of map instead of `Map`. – Semyon Burov Aug 09 '17 at 22:05
  • Me neither, but it was part of the question that the map is specialized for `int` and `long`. – Henri Menke Aug 09 '17 at 22:24
  • Not a great difference but the test proposed by Henri Menke can be used also to enable/disable (via SFINAE) the function; something like `template typename std::enable_if::value && std::is_same< typename Map::mapped_type, long >::value>::type printMap(const Map& map, bool additionalParam = false)` – max66 Aug 10 '17 at 00:59
  • Come on... Even if a type of key and value of a map is valueable for topic starter, the assertion definitely shouldn't be in printing function. May be somewhere else, where there are actual work with elements of map. `printMap` functionality is limited only by existence of `operator<<` overload for key and value types of map – Semyon Burov Aug 10 '17 at 08:05
0

Try this:

template<class Container>
void printMap(const Container& inputMap)
{
    using Key = typename Container::key_type; 
    using Value = typename Container::mapped_type;
    for (const std::pair<Key,Value> p : inputMap)
        std::cout << p.first << "," << p.second << std::endl;
}

or better yet, just:

template<class Container>
void printMap(const Container& inputMap)
{
    for (const auto& p : inputMap)
        std::cout << p.first << ","<< p.second << std::endl;
}
einpoklum
  • 118,144
  • 57
  • 340
  • 684