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 }
Alias templates are a way to give a name to a family of types. Template parameters can be types, non-types, and templates themselves.
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.
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.
So the simple answer is YES.
[...] 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With