Why Are Rvalues References Variables Not Rvalue

Is there a difference between universal references and forwarding references?


Do they mean the same thing?

Universal reference was a term Scott Meyers coined to describe the concept of taking an rvalue reference to a cv-unqualified template parameter, which can then be deduced as either a value or an lvalue reference.

At the time the C++ standard didn't have a special term for this, which was an oversight in C++11 and makes it hard to teach. This oversight was remedied by N4164, which added the following definition to [temp.deduct]:

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.

Hence, the two mean the same thing, and the current C++ standard term is forwarding reference. The paper itself articulates why "forwarding reference" is a better term than "universal reference."

Is it only a forwarding reference if the function body calls std::forward?

Nope, what you do with a forwarding reference is irrelevant to the name. The concept forwarding reference simply refers to how the type T is deduced in:

template <class T> void foo(T&& ); // <== 

It does not need to be subsequently forwarded .

Rvalue reference or lvalue?

The confusion is probably arising from the difference between r-value and r-value reference. The former is a value-category which only applies to expressions, while the latter is a type which applies to variables (technically it would need to be an r-value reference of some type, e.g. r-value reference to int).

So the difference between the snippets you've shown is not actually related to the type of the variable, but the value-category of the expression. Postfix operator++ requires the value-category of the operand to be an l-value, regardless of the type of the operand.

In k++, the expression k is an l-value (roughly speaking, it has a name), which is its value-category. The type of the variable k is an r-value reference, but that's fine.

In (static_cast<int&&>(3))++, the expression static_cast<int&&>(3) is an r-value (it doesn't have a name), which is its value-category. Regardless of the type of static_cast<int&&> (which is int), the value-category is wrong, and so you get an error.

Note that the error message using rvalue as lvalue is referring to the value-category of the expression being used. It has nothing to do with the types of the variables.

Why can i assign a value to a rvalue reference?


why can I assign a value to a rvalue ref like : int&& x = 10

That's not an assignment. That's initialisation. Here, a temporary object is materialised from the integer constant and the reference is bound to that temporary. Same thing can be done with lvalue references to const: const int& x = 10. It cannot be done with lvalue references to non-const since they cannot be bound to rvalues.

doesn't that mean that an rvalue ref is an lvalue

Only expressions have values. int&& x = 10; is a declaration and not an expression. Thus the declaration doesn't have a value in the first place.

The declaration contains an expression 10 which is a prvalue.

Types are not expressions either, so "rvalue reference is an lvalue" is meaningless.

Variables don't have value categories either. However id-expressions - which are just expressions that name a variable - do have a value category. They are lvalues regardless of the type of the named variable - yes, even if the type is rvalue reference.

auto&& variable's are not rvalue reference

Once the type of the initializer has been determined, the compiler determines the type that will replace the keyword auto using the rules for template argument deduction from a function call (see template argument deduction#Other contexts for details). The keyword auto may be accompanied by modifiers, such as const or &, which will participate in the type deduction.

For example, given

const auto& i = expr;

The type of i is exactly the type of the argument u in an imaginary

template template<class U> 
void f(const U& u)

If the function call f(expr) was compiled.

In general , it can be think as below .

 template template<class U> 
void f(paramtype u)

Therefore, auto&& may be deduced either as an lvalue reference or rvalue reference according to the initializer.

In your case , imaginary template would look like

 template template<class U> 
void f(U&& var2){}
f(var1)

Here ,var1 is named rvalue which is being treated as lvalue, so var2 will be deduced as lvalue .

Consider the following examples:

auto&& var2 = widget() ; //var2 is rvalue reference here .
int x=10;
const int cx=10;
auto&& uref1 = x; // x is int and lvalue, so uref1's type is int&
auto&& uref2 = cx; // cx is const int and lvalue, so uref2's type is const int&
auto&& uref3 = 27; // 27 is int and rvalue, so uref3's type is int&&

C++11: Why rvalue reference parameter implicitly converted to lvalue

One, the x argument to fn isn't an r-value reference, it's a "universal reference" (yes, this is rather confusing).

Two, the moment you give an object a name, that name is not an r-value unless explicitly "fixed", either with std::move (to make it an r-value reference, always), or with std::forward (to convert it back to its original type in the case of universal references). If you want to avoid the complaint, use std::forward to forward as the original type:

template <class T>
void fn (T&& x) {
overloaded(std::forward<T>(x));
}

Why is a named rvalue reference an lvalue expression?

Per [expr.prim.id.unqual] (8.1.4.1 Unqualified names):

[...] The expression is an lvalue if the entity is a function,
variable, or data member and a prvalue otherwise; it is a bit-field if
the identifier designates a bit-field ([dcl.struct.bind]).

Per [basic]/6:

A variable is introduced by the declaration of a reference other
than a non-static data member or of an object. The variable's name, if
any, denotes the reference or object.

The declaration

int&& ref2 = std::move(x);

is a "declaration of a reference other than a non-static data member." Therefore, the entity denoted by ref2 is a variable. So the expression ref2 is an lvalue.

Why can some variables of reference type bind rvalues and some can't?

This would indeed not work without the const -- you would get a compilation error.

But the const is there, i.e. you are not going to modify what c is referencing.

For this case, there is additional wording in the standard that the temporary value c is referencing (the result of 1 + 2) will have its lifetime extended to the end of the lifetime of the reference.

This is quite unrelated to auto. It's the const that is making the difference here.



Related Topics



Leave a reply



Submit