SFINAE only applies to things directly within the function signature. The compilation error you get happens within the instantiation of base<T, K>
, which is two steps removed from the function signature.
That said, why do you do this the hard way?
template <typename T, typename U>
struct is_same { static const bool value = false; };
template <typename T>
struct is_same<T, T> { static const bool value = true; };
template <typename T, typename U>
struct is_different { static const bool value = !is_same<T, U>::value; };
Edit:
So, to answer your question in the comment, let me explain a bit more about SFINAE. The standard specifies SFINAE in 17.8.2p8:
If a substitution results in an invalid type or rexpression, type deduction fails. An invalid type or rexpression is one that would be ill-formed if written using the substituted arguments. Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure. [Note: The evaluation of the substituted types and expressions can result in side effects such as the instantiation of the class template specializations and/or function template specializations, the generation of implicitly-defined functions, et. Such side effects are not in the "immediate context" and can result in the program being ill-formed. - end note]
The thing that's problematic here is the "immediate context". You are doing two things here that remove your error from the immediate context.
- The error occurs during evaluation of the default argument expression of
checker
. Although this evaluation is triggered by the occurrence ofchecker<T,K>
in the argument list oftest
, which gets instantiated for overload resolution, it isn't the immediate context. You can try this out by changing the definition ofbase
totemplate <typename T, typename U> struct base {};
. This makes the error in the instantiation ofbase
go away, replacing it with an error in the context of the default argument expression ("does not have a member calledvalue
"). But SFINAE will still not trigger; you'll still get a hard error. - The error occurs in the instantiation of the definition of
base
. This is yet another step removed from overload resolution. For the same reason, putting a member intobase
with typeT::foobar
doesn't work for finding out whetherT
contains a typefoobar
: the definition ofbase
is not a SFINAE context anymore. You can see that this alone is an error by removingchecker
from the picture, e.g. doing this:
template <typename U, typename V> struct base: public id<U>, public id<V> {
typedef void type;
};
template <typename U, typename V> struct is_different {
struct true_ { char c[1]; }; // I don't like sizeof(reference type),
struct false_ { char c[2]; }; // makes me nervous.
template <typename T, typename K>
static true_ test(typename base<T, K>::type*);
template <typename, typename>
static false_ test(...);
static const bool value = sizeof(test<U, V>(0)) == sizeof(true_);
};
Now the base
instantiation doesn't hide behind a template default argument expression, but is right there in the signature, but you'll still get an error.