问题
People are confused when they hear that in
int&& x
x
has rvalue reference type, but x
is an lvalue. Misunderstanding stems from the fact that identifiers and expressions are different things, and so are types and value categories. Moreover, types of expressions are "adjusted prior to any further analysis", and the words "rvalue" and "lvalue" can appear both in type name and in value category name.
I want to clarify formal definitions. Suppose we have a function:
1 | void f(int&& x) {
2 | ... = x;
3 | ... = std::move(x);
4 | }
Are the following statements correct?
- In the line 1,
x
is an identifier (id-expression) that names a function parameter. Its type isint&&
, and this is the type thatdecltype(x)
returns.x
is not an expression and has no value category. - In the line 2,
x
is an expression. Before type adjustment its type isint&&
, and after the type becomesint
. The value category is lvalue. - In the line 3,
std::move(x)
is an expression. Its type before adjustment isint&&
, after -int
. The value category is xvalue. - When we say that
x
has rvalue reference type, we refer either to the type ofx
as an identifier, or to the type ofx
as an expression before type adjustment. - The word "type" in the statement "Each expression has some non-reference type, and each expression belongs to exactly one of the three primary value categories" at cppreference.com refers to the type after type adjustment.
- When Scott Meyers writes "If the type of an expression is an lvalue reference (e.g.,
T&
orconst T&
, etc.), that expression is an lvalue." he refers to the type before adjustment, and the second word "lvalue" refers to the value category.
回答1:
Some preliminary paragraphs first:
[basic]
3 An entity is a value, object, reference, function, enumerator, type, class member, template, template specialization, namespace, parameter pack, or this.
[dcl.type.simple]
4 The type denoted by
decltype(e)
is defined as follows:
if
e
is an unparenthesized id-expression or an unparenthesized class member access ([expr.ref]),decltype(e)
is the type of the entity named bye
. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed;otherwise, if
e
is an xvalue,decltype(e)
isT&&
, whereT
is the type ofe
;otherwise, if e is an lvalue,
decltype(e)
isT&
, whereT
is the type ofe
;otherwise,
decltype(e)
is the type ofe
.[dcl.ref]
1 In a declaration
T D
whereD
has either of the forms& attribute-specifier-seqopt D1 && attribute-specifier-seqopt D1and the type of the identifier in the declaration
T D1
is “derived-declarator-type-listT
,” then the type of the identifier ofD
is “derived-declarator-type-list reference toT
.”[expr]
5 If an expression initially has the type “reference to
T
” ([dcl.ref], [dcl.init.ref]), the type is adjusted toT
prior to any further analysis. The expression designates the object or function denoted by the reference, and the expression is an lvalue or an xvalue, depending on the expression.[expr.prim.general]
8 An identifier is an id-expression provided it has been suitably declared (Clause [dcl.dcl]). The type of the expression is the type of the identifier. The result is the entity denoted by the identifier. The result is an lvalue if the entity is a function, variable, or data member and a prvalue otherwise.
[expr.call]
10 A function call is an lvalue if the result type is an lvalue reference type or an rvalue reference to function type, an xvalue if the result type is an rvalue reference to object type, and a prvalue otherwise.
Which now allows us to answer your questions.
In the line 1,
x
is an identifier (id-expression) that names a function parameter. Its type isint&&
, and this is the type thatdecltype(x)
returns.x
is not an expression and has no value category.
Yes of sorts. x
in the declaration is not an expression. But as an argument to decltype
is an expression. However, it hits the special case of decltype
's first bullet so the type of the identifier named by x
is deduced, instead of the type of x
as an expression.
In the line 2,
x
is an expression. Before type adjustment its type isint&&
, and after the type becomesint
. The value category is lvalue.
Yes.
In the line 3,
std::move(x)
is an expression. Its type before adjustment isint&&
, after -int
. The value category is xvalue.
Yes.
When we say that
x
has rvalue reference type, we refer either to the type ofx
as an identifier, or to the type ofx
as an expression before type adjustment.
Yes.
The word "type" in the statement "Each expression has some non-reference type, and each expression belongs to exactly one of the three primary value categories" at cppreference.com refers to the type after type adjustment.
Yes.
When Scott Meyers writes "If the type of an expression is an lvalue reference (e.g.,
T&
or constT&
, etc.), that expression is an lvalue." he refers to the type before adjustment, and the second word "lvalue" refers to the value category.
Can't really say for sure what Scott Meyers meant when he wrote this, but that is the only interpretation of the words that matches up with the standard, yes.
来源:https://stackoverflow.com/questions/56716647/rvalues-lvalues-and-formal-definitions