Reputation: 22592
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
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