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.