35

I have been using SFINAE-based approaches for quite some time, especially to enable/disable specific class template specializations via std::enable_if.

I was thus a bit puzzled while reading the paper describing the proposed void_t alias / detection idiom:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf

Section 4 is devoted to the discussion of the validity of the idiom, and refers to a discussion in which two parties argue about the applicability of SFINAE in partial class template specializations (with Richard Smith pointing out that the standard is lacking wording about this topic). Towards the end of the section, the following CWG issue is mentioned

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2054

Here again it is stated that the standard does not explicitly allows the example reproduced in the issue.

I am a bit baffled because it seems to me that, e.g., the usage of enable_if in partial specializations has been standard practice for quite some time (see for instance the Boost documentation, which explicitly mentions partial specializations).

Am I misunderstanding the points in the documents above or is this really a grey area?

5
  • 4
    It seems like a decent argument, both that the standard doesn't specify it, and that every implementation handles it as you would like, and that it will almost certainly be resolved the way that matches the status quo? Commented Jun 5, 2015 at 23:17
  • @Yakk but then I am a bit surprised to see it promoted as a portable way of doing things in circles like Boost and stackoverflow. This is a technique that, in earlier forms, dates to C++03, it seems strange it would not be standardised in C++11/C++14.
    – bluescarni
    Commented Jun 5, 2015 at 23:38
  • 3
    Well, probably nobody noticed. Commented Jun 5, 2015 at 23:38
  • 1
    I re-read the relevant sections of the standard, and it seems like a case of something that might benefit an explicit clarification. The issue it that in the section regarding class template spec., 14.5.5.1/2, it basically says "look at 14.8.2" for the actual rules. 14.8.2 indeed does include all the SFINAE stuff, but that one is a section about function templates, not class templates. To me it looks like they didn't want to repeat themselves, thus the redirection - but it does sound pretty clunky.
    – bluescarni
    Commented Jun 6, 2015 at 0:12
  • @Yakk forgot to say thanks for the reply, what you say makes a lot of sense :)
    – bluescarni
    Commented Jun 6, 2015 at 14:28

1 Answer 1

21

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.

2
  • Has anything changed since the time this answer was written?
    – Evg
    Commented May 22, 2021 at 17:49
  • 1
    @Evg I don't think so. Substitution failures and immediate contexts are in general fairly underspecified.
    – Barry
    Commented May 22, 2021 at 18:46

Not the answer you're looking for? Browse other questions tagged or ask your own question.