Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rvalues, lvalues and formal definitions

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?

  1. In the line 1, x is an identifier (id-expression) that names a function parameter. Its type is int&&, and this is the type that decltype(x) returns. x is not an expression and has no value category.
  2. In the line 2, x is an expression. Before type adjustment its type is int&&, and after the type becomes int. The value category is lvalue.
  3. In the line 3, std::move(x) is an expression. Its type before adjustment is int&&, after - int. The value category is xvalue.
  4. When we say that x has rvalue reference type, we refer either to the type of x as an identifier, or to the type of x as an expression before type adjustment.
  5. 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.
  6. When Scott Meyers writes "If the type of an expression is an lvalue reference (e.g., T& or const T&, etc.), that expression is an lvalue." he refers to the type before adjustment, and the second word "lvalue" refers to the value category.
like image 363
Evg Avatar asked Jun 22 '19 15:06

Evg


People also ask

What are Rvalues and Lvalues?

An lvalue refers to an object that persists beyond a single expression. An rvalue is a temporary value that does not persist beyond the expression that uses it.

What are Rvalues?

An rvalue (so-called, historically, because rvalues could appear on the right-hand side of an assignment expression) is an xvalue, a temporary object or subobject thereof, or a value that is not associated with an object. A prvalue (“pure” rvalue) is an rvalue that is not an xvalue.

What are Rvalues in C++?

An rvalue is an expression that is not an lvalue. Examples of rvalues include literals, the results of most operators, and function calls that return nonreferences. An rvalue does not necessarily have any storage associated with it.

What is the difference between L value and R value references?

“l-value” refers to a memory location that identifies an object. “r-value” refers to the data value that is stored at some address in memory. References in C++ are nothing but the alternative to the already existing variable. They are declared using the '&' before the name of the variable.

What is an Invalue in C++?

In C++ an lvalue is some things that point to a selected memory location. On the opposite hand, an rvalue is some things that do not point anywhere. Generally, Rvalues are temporary, while lvalues live an extended life since they exist as variables.


1 Answers

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 by e. 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) is T&&, where T is the type of e;

  • otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;

  • otherwise, decltype(e) is the type of e.

[dcl.ref]

1 In a declaration T D where D has either of the forms

& attribute-specifier-seqopt D1 
&& attribute-specifier-seqopt D1

and the type of the identifier in the declaration T D1 is “derived-declarator-type-list T,” then the type of the identifier of D is “derived-declarator-type-list reference to T.”

[expr]

5 If an expression initially has the type “reference to T” ([dcl.ref], [dcl.init.ref]), the type is adjusted to T 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 is int&&, and this is the type that decltype(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 is int&&, and after the type becomes int. The value category is lvalue.

Yes.

In the line 3, std::move(x) is an expression. Its type before adjustment is int&&, after - int. The value category is xvalue.

Yes.

When we say that x has rvalue reference type, we refer either to the type of x as an identifier, or to the type of x 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 const T&, 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.

like image 70
StoryTeller - Unslander Monica Avatar answered Oct 24 '22 14:10

StoryTeller - Unslander Monica