Why does adding const turn a forwarding reference into an rvalue reference?
Asked Answered
D

1

33

I have been reading about the universal references in Scott's last master piece about the c++11 and 14 with that being said despite an argument assigned to either lvalue or an rvalue type reference parameter there is something in between called universal reference which could deduced to either l/rvalue based on the type trait of an argument that passed . I could understand what makes the parameter as an universal reference but the one thing that doesn't clear to me is why adding const to the type parameter const T&& p make the p as rvalue:

template<typename T>
void f(T&& param); // param is an universal reference

template<typename T>
void f(const T&& param); // param is an rvalue reference

Does the const do more than this when assigned to the reference parameter.

Dunaj answered 7/8, 2016 at 14:9 Comment(0)
I
28

The official name is not universal reference, but forwarding reference. The Standard states that only rvalue references to cv-unqualified template parameters fall in this category:

14.8.2.1 Deducing template arguments from a function call [temp.deduct.call]

3 If P is a cv-qualified type, the top level cv-qualifiers of P’s type are ignored for type deduction. If P is a reference type, the type referred to by P is used for type deduction. A forwarding reference is an rvalue reference to a cv-unqualified template parameter. 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. [ Example:

template <class T> int f(T&& heisenreference);
template <class T> int g(const T&&);
int i;
int n1 = f(i); // calls f<int&>(int&)
int n2 = f(0); // calls f<int>(int&&)
int n3 = g(i); // error: would call g<int>(const int&&), which
               // would bind an rvalue reference to an lvalue

— end example ]

Allowing const T&& to behave as forwarding references, would make it impossible to overload a template function who takes only an rvalue reference as parameter.

Update: as @HowardHinnant mentions in the comments, const T&& does have its uses (see also this Q&A).

Implacable answered 7/8, 2016 at 15:27 Comment(6)
To tie back to the part of Item 24 in Effective Modern C++ that confused the OP, Scott describe (loosely) universal references to be rvalue references in a type deducing context. The first (non-emphasized) sentence in the 14.8.2.1 standard quote above ("If P is a cv-qualified type, the top level cv-qualifiers of P’s type are ignored for type deduction") clearly explains Scott's statement that "Even the simple presence of a const qualifier is enough to disqualify a reference from being universal" (quote from book, Item 24).Cooky
@dfri Thanks, I didn't have a digital copy of EMC++ nearby.Implacable
More motivation as to the why: Sometimes we need to say: Don't bind to an rvalue, only to lvalues: template <class T> void cref(const T&&) = delete;.Floccose
@HowardHinnant updated with a link to another Q&A where you explained that in depth, tnx!Implacable
"allowing const T&& to behave as forwarding references, would lead to binding rvalue references to lvalues.", strictly speaking, this is not true. If const T&& is allowed to use as forwarding reference, we would deduce T as int & in g(i), then collapse to const int &, which binds const lvalue refere to lvalue. The problem is that we would have no way to overload a template function who takes only rvalue reference as parameter if both T && and const T && are allowed to use as forwarding references.Darcidarcia
@Implacable Could you please show a short example (code) illustrating why "allowing const T&& to behave as forwarding references, would make it impossible to overload a template function who takes only an rvalue reference as parameter"? Thank you.Rawson

© 2022 - 2024 — McMap. All rights reserved.