Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

enable_if to check if value type of iterator is a pair

Tags:

c++

c++11

I would like to write a specialised template function for iterators whose value type is a pair. My expectation is that this should match iterators of std::map.

In order to detect the pair:

template <typename>
struct is_pair : std::false_type
{ };

template <typename T, typename U>
struct is_pair<std::pair<T, U>> : std::true_type
{ };

// also tried this, but it didn't help
template <typename T, typename U>
struct is_pair<std::pair<const T, U>> : std::true_type
{ };

Then i use enable_if in the function declaration:

template<class ITR>
decltype(auto) do_stuff(
        std::enable_if<is_pair<typename ITR::value_type>::value, ITR> itr) {
    //access of itr->second ok.
}

However, when i use this function with a map iterator I get the following error messages from clang (Xcode 8.3):

Candidate template ignored: could not match 'enable_if' against '__map_iterator'

With no further explanation why enable if didn't match.

When inspecting the type of __map_iterator, it looks like it should match is_pair check.

like image 436
JE42 Avatar asked May 16 '17 04:05

JE42


2 Answers

It's better to move std::enable_if to another default template argument, like so:

template<class ITR, typename = typename std::enable_if<is_pair<typename ITR::value_type>::value, ITR>::type>
decltype(auto) do_stuff(ITR && itr) {
    //access of itr->second ok.
}

This will not block argument deduction, since ITR && itr is now an universal reference.

Full example:

#include <type_traits>
#include <utility>
#include <map>

template <typename>
struct is_pair : std::false_type
{ };

template <typename T, typename U>
struct is_pair<std::pair<T, U>> : std::true_type
{ };


template<class ITR, typename = typename std::enable_if<is_pair<typename ITR::value_type>::value, ITR>::type>
decltype(auto) do_stuff(ITR && itr) {
    //access of itr->second ok.
}

int main()
{
    std::map<int, int> foo{
        { 1, 2 },
        { 3, 4 },
    };

    do_stuff(foo.begin());
    return 0;
}

Live on gcc.godbolt.org

like image 63
GreenScape Avatar answered Oct 26 '22 23:10

GreenScape


I figured it out, essentially it was: SFINAE working in return type but not as template parameter

So in the end i used this signature:

template<class ITR>
typename std::enable_if_t<is_pair<typename ITR::value_type>::value, ITR>::value_type::second_type& get_value_from_iterator(ITR itr)

The reference was required because i needed an actual reference to the value inside the map.

The const version of the check wasn't necessary.

like image 28
JE42 Avatar answered Oct 26 '22 22:10

JE42