Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SFINAE and partial class template specializations

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?

like image 449
bluescarni Avatar asked Jun 05 '15 22:06

bluescarni


People also ask

What is meant by template specialization?

The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.

What is Sfinae used for?

Substitution failure is not an error (SFINAE) refers to a situation in C++ where an invalid substitution of template parameters is not in itself an error. David Vandevoorde first introduced the acronym SFINAE to describe related programming techniques.

Will concepts replace Sfinae?

So the simple answer is YES.


1 Answers

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.

like image 140
Barry Avatar answered Sep 18 '22 12:09

Barry