Reputation: 1091
Is "arg" an universal/forwarding-reference or an rvalue-reference?
template<auto&& arg>
struct test {};
Upvotes: 13
Views: 862
Reputation: 119641
Forwarding references exist only for the purposes of [temp.deduct.call] and [temp.deduct.type] (will not be discussed here).
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 toA
" is used in place ofA
for type deduction.
In template <auto&& arg> struct test {};
, the reference arg
is not a forwarding reference. However, when a template argument is supplied for arg
, [temp.arg.nontype]/1 tells us to invent a declaration
auto&& x = E;
and the type of arg
is the type of x
. To find the type of x
, we have to use [dcl.type.auto.deduct]/3, which then defers to [temp.deduct.call] with an invented function parameter type U&&
. Here, P
is U&&
and is a forwarding reference. The template parameter U
does not represent a template parameter of a class template; instead, U
was obtained by "[replacing] [...] auto
[...] with a new invented type template parameter", following the wording in [dcl.type.auto.deduct]/3.
Upvotes: 6
Reputation: 96959
It is a forwarding (aka universal) reference.
It's easy to check:
// (This needs to be at namespace scope, addresses local variables can't
// be passed to reference or pointer template arguments.).
int x;
test<x> t;
This compiles despite x
being an lvalue.
As noted by @user12002570, the standard apparently has a very narrow definition of a "forwarding reference" that only applies to function parameters, which means this technically isn't one (@Brian Bi's answer discusses this in more detail). But I'd argue the choice of words is lowkey defective, this isn't the definition that people usually use.
Usually people say "a forwarding reference" to indicate that a seemingly rvalue reference can become an lvalue reference if given an lvalue, and the answer in this case is yes.
Upvotes: 16
Reputation: 1
Is "arg" an universal/forwarding-reference or an rvalue-reference?
As declared, arg
is not a forwarding reference as per temp.deduct.call:
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])).
Note the emphasis in bold which makes this not a universal/forwarding reference.
Note that cppreference's documentation on forwarding reference contradicts with the c++ standard here. In particular, cppreference page says:
Forwarding references are a special kind of references that preserve the value category of a function argument, making it possible to forward it by means of std::forward. Forwarding references are either:
- function parameter of a function template declared as rvalue reference to cv-unqualified type template parameter of that same function template:
auto&&
except when deduced from a brace-enclosed initializer list:auto&& vec = foo(); // foo() may be lvalue or rvalue, vec is a forwarding reference
Upvotes: 1