The following code comes from cxx-prettyprint , which implements detecting whether type T has a corresponding member
#include<iostream>
#include<vector>
#include<type_traits>
using namespace std;
struct sfinae_base
{
using yes = char;
using no = yes[2];
};
template <typename T>
struct has_const_iterator : private sfinae_base
{
private:
template <typename C> static yes& test(typename C::const_iterator*);
template <typename C> static no& test(...);
public:
static const bool value = sizeof(test<T>(nullptr)) == sizeof(yes);
using type = T;
};
template <typename T>
struct has_begin_end : private sfinae_base
{
private:
template <typename C>
static yes& f(typename std::enable_if<
std::is_same<decltype(static_cast<typename C::const_iterator(C::*)() const>(&C::begin)),
typename C::const_iterator(C::*)() const>::value>::type*);
template <typename C> static no& f(...);
template <typename C>
static yes& g(typename std::enable_if<
std::is_same<decltype(static_cast<typename C::const_iterator(C::*)() const>(&C::end)),
typename C::const_iterator(C::*)() const>::value, void>::type*);
template <typename C> static no& g(...);
public:
static bool const beg_value = sizeof(f<T>(nullptr)) == sizeof(yes);
static bool const end_value = sizeof(g<T>(nullptr)) == sizeof(yes);
};
int main()
{
vector<int> sa{ 1,2,3,4,5 };
cout << has_const_iterator<vector<int>>::value;
cout<<has_begin_end<vector<int>>::beg_value;
cout << has_begin_end<vector<int>>::end_value;
return 0;
}
Some time later I read someone else's blog and changed it to this
#include<utility>
#include<iostream>
#include<vector>
using namespace std;
template <typename T>
struct has_const_iterator
{
private:
template <typename U>
static constexpr decltype(std::declval<U::const_iterator>(), bool()) test(int) { return true; }
template <typename U>
static constexpr bool test(...) { return false; }
public:
static const bool value = test<T>(1); //为什么这个不对?
using type = T;
};
template <typename T>
struct has_begin_end
{
private:
template <typename U>
static constexpr decltype(std::declval<U>().begin(), bool()) f(int) { return true; }
template <typename U>
static constexpr bool f(...) { return false; }
template <typename U>
static constexpr decltype(std::declval<U>().end(), bool()) g(int) { return true; }
template <typename U>
static constexpr bool g(...) { return false; }
public:
static bool const beg_value = f<T>(2);
static bool const end_value = g<T>(2);
};
int main()
{
vector<int> sa{ 1,2,3,4,5 };
cout << has_const_iterator<vector<int>>::value;
cout<<has_begin_end<vector<int>>::beg_value;
cout << has_begin_end<vector<int>>::end_value;
return 0;
}
For the first piece of code it shows 111
For the second piece of code it shows 011
Snippet 2 was working fine a few months ago, but not now.
My question is, what's wrong with the second one,and why it was good before and now goes wrong?
Added: I found this, what does he mean by introduce ODR violations
?
CodePudding user response:
If you remove all unnecessary code and then try to call the overload of has_const_iterator::test
that returns true
(by commenting out the other):
#include <utility>
#include <vector>
#include <iostream>
template <typename T>
struct has_const_iterator
{
private:
template <typename U>
static constexpr decltype(std::declval<U::const_iterator>(), bool()) test(int) { return true; }
//template <typename U>
//static constexpr bool test(...) { return false; }
public:
static const bool value = test<T>(1);
};
int main()
{
std::cout << has_const_iterator<std::vector<int>>::value;
}
then you get this error message (I cleaned it up a bit):
error: dependent-name 'U::const_iterator' is parsed as a non-type, but instantiation yields a type
| static constexpr decltype(std::declval<U::const_iterator>(), bool()) test(int) { return true; }
note: say 'typename U::const_iterator' if a type is meant
It tells you exactly what's wrong: typename
is missing before U::const_iterator
.
This version works:
#include <utility>
#include <vector>
#include <iostream>
template <typename T>
struct has_const_iterator
{
private:
template <typename U>
static constexpr decltype(std::declval<typename U::const_iterator>(), bool()) test(int) { return true; }
template <typename U>
static constexpr bool test(...) { return false; }
public:
static const bool value = test<T>(1);
};
int main()
{
std::cout << has_const_iterator<std::vector<int>>::value;
}
CodePudding user response:
You are missing a typename
because U::const_iterator
is a dependant name.
template <typename T>
struct has_const_iterator
{
private:
template <typename U>
static constexpr decltype(std::declval<typename U::const_iterator>(), bool()) test(int) { return true; }
// ^^
template <typename U>
static constexpr bool test(...) { return false; }
public:
static const bool value = test<T>(1); //为什么这个不对?
using type = T;
};
After fixing that it prints expected results: https://godbolt.org/z/jP9G95Pb1l.
I did not find yet a compiler that produced output 111
without the typename though.