Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are rvalues references variables not rvalue?

Let's say I have two overloads of a function f

void f(T&&); // #1
void f(T&);  // #2

Then in the body of another function g

void g(T&& t) 
{ 
  f(t);  // calls #2
}

the overload f(T&) will be called because t is considered an lvalue.

This is very surprising to me. How a function with signature f(T&&) cannot match a call with type T&&? What surprises me even more is that a call f(static_cast<T&&>(t)) would actually call the rvalue overload f(T&&).

What are the C++ rules that make this possible? Is T&& more than a type?

like image 368
ElefEnt Avatar asked Sep 17 '15 00:09

ElefEnt


People also ask

Is an rvalue reference an rvalue?

An rvalue reference is formed by placing an && after some type. An rvalue reference behaves just like an lvalue reference except that it can bind to a temporary (an rvalue), whereas you can not bind a (non const) lvalue reference to an rvalue.

Can lvalue reference bind rvalue?

An lvalue const reference can bind to an lvalue or to an rvalue. The syntax for a reference to an rvalue of type T is written as T&& . An rvalue reference refers to a movable value—a value whose contents we don't need to preserve after we've used it (for example, a temporary).

Can lvalue be rvalue?

An lvalue (3.10) of a non-function, non-array type T can be converted to an rvalue.

How do you pass rvalue reference to a function?

If you want pass parameter as rvalue reference,use std::move() or just pass rvalue to your function.


1 Answers

The things that are automatically treated as rvalues are things without names, and things that (very shortly) will not have a name (in the return value case).

T&& t has a name, it is t.

The reason why rvalues are those things is that referring to them after that point of use is next to impossible.

T&& is the type rvalue reference. An rvalue reference can only bind to an rvalue (without a static_cast being involved), but it is otherwise an lvalue of type rvalue reference.

The fact it is of type rvalue reference only matters during its construction, and if you do decltype(variable_name). It is otherwise just another lvalue of reference type.

std::move(t) does a return static_cast<T&&>(t); and returns an rvalue reference.

The rules that govern this are written in standardese in the C++ standard. A copy/paste of them won't be all that useful, because they are not that easy to understand.

The first general rule is, you get an implicit move (aka, parameter that binds to an rvalue reference argument) when you return a named value from a function, or when a value has no name, or when a function explicitly returns an rvalue reference.

Second, that only rvalue references and const& can bind to rvalues.

Third, reference lifetime extension on temporary values occurs when directly bound to a reference outside of a constructor. (as only rvalue references and const& can directly bind to a temporary, this only applies to them)

Forth, T&& isn't always an rvalue reference. If T is of type X& or X const&, then reference collapsing turns T&& into X& or X const&.

Finally, T&& in a type deduction context will deduce T as X, X&, X const& or X const&& depending on the type of the argument, and hence can act as a "forwarding reference".

like image 159
Yakk - Adam Nevraumont Avatar answered Sep 26 '22 00:09

Yakk - Adam Nevraumont