A very good read is Value categories.
Yes, the type of the variable r
is indeed int&&
. However what matters here is the expression and:
Each C++ expression (an operator with its operands, a literal, a
variable name, etc.) is characterized by two independent properties: a
type and a value category. Each expression has some non-reference
type, and each expression belongs to exactly one of the three
primary value categories: prvalue, xvalue, and lvalue.
The expression r
is an lvalue:
lvalue
The following expressions are lvalue expressions:
- the name of a variable [...], regardless of type. Even if the variable's type is rvalue reference, the expression consisting of its
name is an lvalue expression;
Rvalue references can bind to prvalues or xvalue but not to lvalues so if you want to bind an rvalue reference to r
you need to convert r
to an xvalue. This is done with std::move
which despite its name is just an cast.
You can easily reason about this like so: if it has a name then it's an lvalue (even if the type of that id is rvalue reference). You can't bind rvalue references (which in principle should bind to temporary objects) to something that has a name. Something that has a name can be reused. You need std::move
to enable move from that lvalue.
Regarding the message which says "no known conversion from 'int'". As shown above the type of the expression r
is int
, however a more appropriate diagnostic message would have been something along the lines: "rvalue reference cannot bind to lvalue".
And indeed newer clang and gcc give better messages:
gcc
error: cannot bind rvalue reference of type 'int&&' to lvalue of type 'int'
clang
candidate function not viable: expects an rvalue for 1st argument