I came across the Member Detector idiom in C++, which is a type-trait to tell if a class contains a member of a certain name. But the linked example does not work as I expected if the type is not a class: I wanted a false
result for any non-class type. A possible solution is certainly something like this, using boost::is_class<T>
:
template<typename T>
struct general_DetectX : boost::mpl::and_<
boost::is_class<T>,
DetectX<T> >::type
{ };
bool hasX = general_DetectX<int>::value; // hasX = false
But this question is about why the original DetectX<T>
produces errors instead of doing the SFINAE thing. Here is an excerpt of the relevant parts of the linked code (local structs Fallback
and Check<U,U>
and typedefs ArrayOfOne
, ArrayOfTwo
and type
removed for brevity):
template<typename T>
class DetectX
{
struct Derived : T, Fallback { };
template<typename U>
static ArrayOfOne & func(Check<int Fallback::*, &U::X> *);
template<typename U>
static ArrayOfTwo & func(...);
public:
enum { value = sizeof(func<Derived>(0)) == 2 };
};
It can be seen that DetectX::Derived
is used outside of any overload resolution, so the SFINAE rule for handling errors is never invoked. But this can be changed to where use of Derived
does happen as part of the overload resolution:
template<typename T>
class DetectX
{
template<typename U>
struct Derived : U, Fallback { };
template<typename U>
static ArrayOfOne & func(Check<int Fallback::*, &Derived<U>::X> *);
template<typename U>
static ArrayOfTwo & func(...);
public:
enum { value = sizeof(func<T>(0)) == 2 };
};
The Derived<T>
template is only instantiated when trying to instantiate the first func()
overload, so why do I still get errors even for the modified version? Here is an example:
$ g++ --version | head -n1
g++ (GCC) 4.8.2
$ g++ -c demo.cxx
demo.cxx: In instantiation of 'struct DetectX<int>::Derived<int>':
demo.cxx:16:53: required by substitution of 'template<class U> static char (& DetectX<T>::func(DetectX<T>::Check<int DetectX<T>::Fallback::*, (& DetectX<T>::Derived<U>::X)>*))[1] [with U = U; T = int] [with U = int]'
demo.cxx:24:31: required from 'class DetectX<int>'
demo.cxx:27:25: required from here
demo.cxx:7:12: error: base type 'int' fails to be a struct or class type
struct Derived : U, Fallback { };
^