I was about to ask the same question, but it has already been asked. Unfortunately, none of the existing answers answered the question. Not for me, at least. I had to figure it out. I wanted to ask the same question as the OP, plus some. My question is: WTF is is_class_tester (void (U::*)(void)) stuff, and how does this construct work in the SFINAE context (replacement error is not an error)?
With some simplification, Boost uses the construct as follows:
template <typename U> char is_class_tester (void (U::*)(void)); template <typename U> TypeBiggerThanChar is_class_tester (...); template <typename T> struct IsClass { static const bool value = sizeof (is_class_tester<T>(0)) == 1; };
Some observations:
- These functional patterns are not really functional patterns. These are just cutting-edge ads for a couple of overloaded function templates. Function templates themselves are never defined. Seeing that this is so, and understanding how it works without the need to create patterns ever defined is one of the key elements in understanding this design.
- The answers that talked about how to use this first function template missed the boat. You cannot use this function template because its definition does not exist.
- Note that thanks to this argument, the first of the two function templates makes sense only when the type
T is a class. Fundamental types and pointers do not have member functions. The first declaration is invalid syntax for nonclassical types. - Contrast this with the second of the overloaded function templates, which is a valid syntax for all template parameters, and the function (if it existed) will accept any arguments superimposed on it thanks to its variadic argument .... (Other than that: it vaguely reminds me of favorite single-line C program that can solve any problem in the world, given properly formatted user input.)
- While function template declarations cannot be used as functions, declarations can be used in simple compile-time queries, such as a return type request. An actual definition is not required for this kind of query. Only a prototype is required.
- This is exactly what the
IsClass class template IsClass to determine the compile time constant IsClass<SomeType>::value .
So how IsClass<SomeType>::value get its value and how does it do it at compile time? The compiler should either make sense of sizeof (is_class_tester<T>(0)) , or abandon the attempt. We need to look at two cases, based on whether the type is SomeType or not a class.
Case 1: SomeType is a class.
Here, both template declarations are valid syntax, so the compiler has two viable candidates to choose from. The compiler can and should have the first function template, because the selection rules dictate that the variational function receives the lowest priority in the selection. This selected function returns char. Since sizeof (char) is guaranteed to be 1, IsClass<SomeType>::value will be true if SomeType is a class.
Case 2: SomeType not a class.
This is where SFINAE is called. Declaring the first function is invalid syntax. The compiler cannot just abandon it because of SFINAE. He should continue to look for an alternative, and the second declaration of the function template matches the bill. The only viable function returns TypeBiggerThanChar , the definition is gone, but hopefully obvious. All we want is sizeof () this thing. This is more than char, so IsClass<SomeType>::value will be false if SomeType not a class.
David hammen
source share