Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function template receiving any standard map

I am writing a function that should receive one of (std::map, std::multimap, std::unordered_map or std::unordered_multimap). My code is as follow:

template<template <class, class> class Map, typename Coord>
    inline typename std::enable_if<std::is_arithmetic<Coord>::value>::type 
    filter(Map<Coord, Coord>& map, Coord step = 2) {
            for (auto it = std::begin(map); it != std::end(map);) {
                if (it->second - it->first <= step){
                    it = map.erase(it);
                }
                else
                    ++it;
            }
        }

The template template parameter Map does not generalize for all types of maps. The std::map and std::multimap receive four template parameters, and std::unordered_map and std::unordered_multimap receive five template parameters. This implies that I can not solve the problem with a template template parameter. Is there any way to solve this with the constraint that all maps must have KeyType = ValeType = Coord? I would not like to specify explicitly the parameters types in a call to filter.

like image 645
Raul Alonso Avatar asked May 21 '15 13:05

Raul Alonso


3 Answers

You tagged c++11 so I would try to use variadic template parameters:

template<template <class ... > class Map, typename Coord, typename ... MapParms >
inline typename std::enable_if<std::is_arithmetic<Coord>::value>::type
filter(Map<Coord, Coord, MapParms... >& map, Coord step = 2)  
{   
    for (auto it = std::begin(map); it != std::end(map);) 
    {   
        if (it->second - it->first <= step)
        {   
            it = diagram.erase(it);
        }   
        else
        {   
            ++it;
        }   
    }   

}   
like image 188
Klaus Avatar answered Oct 16 '22 22:10

Klaus


Just write your own type trait:

template <typename M, typename COORD>
struct is_map_of_coord : std::false_type { };

That you then specialize for the 5 maps:

template <typename COORD, typename C, typename A>
struct is_map_of_coord<std::map<COORD, COORD, C, A>, COORD>
: std::true_type { };

template <typename COORD, typename H, typename K, typename A>
struct is_map_of_coord<std::unordered_map<COORD, COORD, H, K, A>, COORD>
: std::true_type { };

etc.

Which lets you accept any map, regardless of its allocator, comparator, hash function, etc, as long as it's Key/Value are Coord:

template <typename Map, typename Coord = int>
inline typename std::enable_if<
    std::is_arithmetic<Coord>::value &&
    is_map_of_coord<Map, Coord>::value
>::type 
filter(Map& map, Coord step = 2) {
    /* body */
}

Or you can shorten that by just assuming that since all the maps have a key_type and mapped_type, verify that those both exist and are the same as COORD:

template <typename...>
using void_t = void;

template <typename M, typename COORD, typename = void>
struct is_map_of_coord : std::false_type { };

template <typename M, typename COORD>
struct is_map_of<M, K, void_t<
    std::enable_if_t<std::is_same<typename M::key_type, COORD>::value>,
    std::enable_if_t<std::is_same<typename M::mapped_type, COORD>::value>
> >
: std::true_type
{ };
like image 32
Barry Avatar answered Oct 16 '22 21:10

Barry


Although those template classes take a different number of arguments, they have defaults, so can all be instantiated with just two. As such, you could just use a variadic template, then anything which isn't instantiatable with two arguments will result in a compiler error anyway:

template<template <class...> class Map, typename Coord>
like image 1
TartanLlama Avatar answered Oct 16 '22 21:10

TartanLlama