Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using alias templates for sfinae: does the language allow it?

I have just discovered the following technique. It looks very close to one of proposed concepts syntax, works perfectly on Clang, GCC and MSVC.

template <typename T, typename = typename std::enable_if<std::is_rvalue_reference<T&&>::value>::type> using require_rvalue = T&&;  template <typename T> void foo(require_rvalue<T> val); 

I tried to find it with search requests like "sfinae in type alias" and got nothing. Is there a name for this technique and does the language actually allows it?


The full example:

#include <type_traits>  template <typename T, typename = typename std::enable_if<std::is_rvalue_reference<T&&>::value>::type> using require_rvalue = T&&;  template <typename T> void foo(require_rvalue<T>) { }  int main() {     int i = 0;     const int ic = 0;     foo(i);            // fail to compile, as desired     foo(ic);           // fail to compile, as desired     foo(std::move(i)); // ok     foo(123);          // ok } 
like image 351
Nikita Kniazev Avatar asked Aug 28 '18 01:08

Nikita Kniazev


People also ask

What is an alias template?

Alias templates are a way to give a name to a family of types. Template parameters can be types, non-types, and templates themselves.

What is an alias c++?

A reference variable is an alias, that is, another name for an already existing variable. Once a reference is initialized with a variable, either the variable name or the reference name may be used to refer to the variable.

What is meant by SFINAE?

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

[...] does the language actually allows it?

Can't say anything about the name, but this seems to me to be a yes.

The relevant wording is [temp.alias]/2:

When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template.

and the sfinae rule, [temp.deduct]/8:

Only invalid types and expressions in the immediate context of the function type, its template parameter types, and its explicit-specifier can result in a deduction failure.

Taking an argument of type require_rvalue<T> does behave as if we substitute that alias, which either gives us a T&& or a substitution failure - and that substitution failure is arguably in the immediate context of the substitution and so is "sfinae-friendly" as opposed to being a hard error. Note that even though the defaulted type argument is unused, as a result of CWG 1558 (the void_t rule), we got the addition of [temp.alias]/3:

However, if the template-id is dependent, subsequent template argument substitution still applies to the template-id.

This ensures that we still substitute into the defaulted type argument to trigger the required substitution failure.

The second unsaid part of the question is whether this actually can behave as a forwarding reference. The rule there is in [temp.deduct.call]/3:

A forwarding reference is an rvalue reference to a cv-unqualified template parameter that does not represent a template parameter of a class template (during class template argument deduction ([over.match.class.deduct])). If P is a forwarding reference and the argument is an lvalue, the type “lvalue reference to A” is used in place of A for type deduction.

Is an alias template with one template parameter whose associated type is an rvalue reference to its cv-unqualified template parameter considered a forwarding reference? Well, [temp.alias]/2 says that require_rvalue<T> is equivalent to T&&, and T&& is the right thing. So arguably... yeah.

And all the compilers treat it as such, which is certainly a nice validation to have.


Although, note the existence of CWG 1844 and the lack of actual definition for immediate context, and the example there which also relies upon a substitution failure from a defaulted argument - which the issue states has implementation divergence.
like image 115
Barry Avatar answered Sep 21 '22 05:09

Barry