3

I'm trying to wrap my head around SFINAE. We're using it to check whether a class has a method called "Passengers".

With some online examples, we constructed the following template classes.

#ifndef TYPECHECK
#define TYPECHECK

#include "../Engine/carriage.h"

namespace TSS{

template<typename T>
class has_passengers{
private:
    typedef char one;
    typedef struct{char a[2];} two;

    template<typename C> static one test( decltype(&C::Passengers) );
    template<typename C> static two test(...);
public:
    static bool const value = sizeof(test<T>(0)) == sizeof(one);
};

template<typename T>
struct CarriageTypeCheck{
    static_assert(has_passengers<T>::value, "Train initialized with illegal carriage");
};

}


#endif // TYPECHECK

I get the part how either of the two test-methods is chosen, but what I don't understand is why test<T> is initialized with 0 in the following line:

    static bool const value = sizeof(test<T>(0)) == sizeof(one);

I can not see how the 0 is important for the check to work. Another thing - why is decltype used?

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
Benjamin Larsen
  • 345
  • 2
  • 15

2 Answers2

2

The first overloaded function (potentially) takes a pointer to a class method, as a parameter. Due to C++'s legacy from C, the value 0 is convertible to a NULL pointer, or nullptr. So, if SFINAE does not kick out the first overloaded function, test<T>(0) becomes a valid function call, and its sizeof is equal to sizeof(one). Otherwise this resolves to the second overloaded function call.

And the short answer as to why decltype is used: otherwise it would not be valid C++. In a function declaration, the function's parameter must be types, specifying the types of the parameters to the function. &C::Passengers is not a type (in the context of its intended use), so this would not be valid C++. decltype() of that automatically obtains the type of its argument, making it valid C++.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • Alright. But wouldn't it be equal to 'sizeof(two)' in case T doesn't have Passengers? Then the call would be test(nullpointer) and the size would then be 2? – Benjamin Larsen Oct 30 '16 at 13:56
  • @BenjaminLarsen If T doesn't have Passengers, type deduction would fail, the function template will be ignored; that's how SFINAE works. There's no such rule to pass null pointer for this case, without SFINAE, you'll get a complie error. – songyuanyao Oct 30 '16 at 14:03
  • I think I got it now! Thank you guys. – Benjamin Larsen Oct 30 '16 at 14:05
1

I can not see how the 0 is important for the check to work.

Because 0 could be used as the argument for both cases (i.e. the two overloaded test); for member pointer (treated as null pointer) and variadic arguments ....

Another thing - why is decltype used?

decltype is used for describing the type of member pointer (i.e. &C::Passengers) as the parameter of test.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405