Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
288 views
Welcome To Ask or Share your Answers For Others

1 Answer

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...