Morwenn
Morwenn

Reputation: 22592

Nested member type recognition

Today, I was trying to perform a type switch on whether some class has the nested type reverse_iterator or not. I found on these fora some working solution, which is the following one:

template<typename T>
struct is_reverse_iterable
{
    using yes   = uint8_t;
    using no    = uint16_t;

    template<typename U>
    static yes& test(typename U::reverse_iterator*);

    template<typename>
    static no& test(...);

    static constexpr bool value = sizeof(test<T>(0)) == sizeof(yes);
};

This class works just fine if I simply check the condition from the main, however, I also wrote that little function, which cause some problems.

template<typename T>
void foo(T&& iter)
{
    std::cout << typeid(T).name() << std::endl;
    std::cout << is_reverse_iterable<T>::value << std::endl;
}

Here is the main that causes me some problems:

int main()
{
    using namespace std;

    vector<int> v;

    cout << typeid(decltype(v)).name() << endl;
    cout << is_reverse_iterable<decltype(v)>::value << endl;

    foo(v);

    return 0;
}

As std::vector<int> contains the nested type name reverse_iterator, one would think - or at least, I would think - that is_reverse_iterable<vector<int>>::value would return true wherever I put it. But it's not the case. Here is the result of the main above:

St6vectorIiSaIiEE
1
St6vectorIiSaIiEE
0

When called from the main, the struct is_reverse_iterable recognized the name reverse_iterator in vector<int>, but it did not do so when called from foo. Actually, I have no idea why, and I would like someone to please explain to me what the problem is :)

P.S. : I use MinGW g++ 4.7.1 to compile, with the option -std=c++11.

Upvotes: 0

Views: 193

Answers (1)

kennytm
kennytm

Reputation: 523544

The problem is that when you call foo(v), it is deduced that T is of type std::vector<int>& (an lvalue-reference), so typename T::reverse_iterator will not compile. You can easily check it yourself:

template<typename T>
void foo(T&& iter)
{
    std::cout << typeid(T).name() << std::endl;
    std::cout << is_reverse_iterable<T>::value << std::endl;

    typename T::reverse_iterator t;  // <-- add this line to see what's wrong
}

Yields:

3.cpp: In instantiation of ‘void foo(T&&) [with T = std::vector<int>&]’:
3.cpp:40:10:   required from here
3.cpp:27:34: error: ‘std::vector<int>&’ is not a class, struct, or union type

The solution is simple: remove the reference before you start the SFINAE, e.g.

static constexpr bool value = sizeof(test<typename std::decay<T>::type>(0))
                                == sizeof(yes);

Upvotes: 1

Related Questions