Given that Widget&& var1
is an rvalue reference, why isn't it an rvalue?
someWidget
, var1
, and var2
all have a name and, therefore, they are all lvalues regardless of their declared type.
The term rvalue reference is about the type of the reference, var1
, whereas the term rvalue is about the value category of the expression var1
:
var1
declared as Widget&& var1
is a reference to Widget
, specifically an rvalue reference to Widget
(i.e., Widget&&
) – This is the type of var1
, which is a reference. The reference type tells you how the reference can be initialized: an rvalue reference can only be initialized with an rvalue, whereas a non-const
lvalue reference can only be initialized with an lvalue.
When using var1
in an expression (without marking it with std::move
), then var1
is an lvalue because it has a name – This the value category of var1
, which is a property of the expression var1
orthogonal to its type.
Note also that the following statement doesn't compile:
Widget&& var1 = someWidget;
This compilation error is because var1
is an rvalue reference and, therefore, can only be initialized with an rvalue. However, someWidget
is an lvalue since it has a name, and it isn't marked with std::move
for moving. For the statement to compile you could have done one of the following:
Declaring v1
as an lvalue reference instead:
Widget& var1 = someWidget;
The lvalue reference var1
can be initialized with someWidget
, an lvalue.
Marking someWidget
for moving with std::move()
:
Widget&& var1 = std::move(someWidget);
The rvalue reference var1
can be initialized with std::move(someWidget)
, an rvalue.
Why does auto&& var2
not mean rvalue reference?
var2
is a universal reference because there is type deduction involved. As such, var2
will become either an lvalue reference or rvalue reference depending on how it is initialized (var2
will always be a reference).
The auto
in auto&& var2 = var1;
deduces to Widget&
because var1
is an lvalue (i.e., var1
is a named object and you haven't applied std::move()
to it). Then, Widget& &&
results in Widget&
due to reference collapsing. To sum up, the statement:
auto&& var2 = var1;
after type deduction becomes:
Widget& var2 = var1;
So var2
is actually an lvalue reference.
If you want var2
to be an rvalue reference, you can apply std::move()
to the initializing object:
auto&& var2 = std::move(var1);
after type deduction, this results in:
Widget&& var2 = var1;
var2
is an rvalue reference in this case.