I would like to argue that the Standard does not support SFINAE in partial specializations, due to a wording defect. Let's start with [temp.class.spec.match]:
A partial specialization matches a given actual template argument list if the template arguments of the
partial specialization can be deduced from the actual template argument list (14.8.2).
And, from [temp.deduct], the SFINAE clause:
If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is
one that would be ill-formed, with a diagnostic required, if written using the substituted arguments. [ Note:
If no diagnostic is required, the program is still ill-formed. Access checking is done as part of the substitution
process. —end note ] Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure.
The slightly-modified example† from CWG is:
template <class T, class U> struct X {
typedef char member;
};
template<class T> struct X<T,
typename enable_if<(sizeof(T)>sizeof(
float)), float>::type>
{
typedef long long member;
};
int main() {
cout << sizeof(X<char, float>::member);
}
Name lookup on X
finds the primary, with T == char, U == float
. We look at the one partial specialization, and see if it "matches" - which means that the template arguments "can be deduced" - which is to say:
+-------------+--------+-------------------------------------------------+
| | arg1 arg2 |
+-------------+--------+-------------------------------------------------+
| deduce T in | T | enable_if_t<(sizeof(T) > sizeof(float), float> |
| from | char | float |
+-------------+--------+-------------------------------------------------+
Normal template deduction rules apply. The second "argument" is a non-deducible context, so we deduce T
as char
. sizeof(char) > sizeof(float)
, is false, and enable_if_t<false, float>
is an invalid type, so type deduction should fail... but, deduction failure can only occur
in the immediate context of the function type and its template parameter types
and we're not dealing with a function type or function template parameter types, we're dealing with class template parameter types. A class is not a function, so the SFINAE exclusion should not apply if we take everything literally - and the modified CWG example should lead to a hard error.
However, the spirit of the rule seems to be more along the lines of:
Only invalid types and expressions in the immediate context of the deduction process can result in a deduction failure.
I do not know what the reason would be to specifically exclude class partial specialization deduction. Furthermore, partial ordering of class template partial specializations also look like functions. From [temp.class.order]:
For two class template partial specializations, the first is more specialized than the second if, given the
following rewrite to two function templates, [...]
The Standard thus already in the very next section exhibits a duality between class template partial specializations and function templates. The fact that this only applies to partial specialization ordering, and not substitution failure during partial specialization argument deduction, strikes me as a defect.
†The example itself was X<double, float>
. But this actually doesn't demonstrate or require SFINAE, as there would be no substitution failure anywhere.