views:

1057

answers:

5

In C++03, an expression is either an rvalue or an lvalue.

In C++0x, an expression can be an:

  1. rvalue
  2. lvalue
  3. xvalue
  4. glvalue
  5. prvalue

Two categories have become five categories.

  • What are these new categories of expressions?
  • How do these new categories relate to the existing rvalue and lvalue categories?
  • Are the rvalue and lvalue categories in C++0x the same as they are in C++03?
  • Why are these new categories needed? Are the WG21 gods just trying to confuse us mere mortals?
+44  A: 

I guess this document might serve as a not so short introduction : n3055

The whole massacre began with the move semantics. Once we have expressions that can be moved and not copied, suddenly easy to grasp rules demanded distinction between expressions that can be moved, and in which direction.

From what I guess based on the draft, the r/l value distinction stays the same, only in the context of moving things get messy.

Are they needed? Probably not if we wish to forfeit the new features. But to allow better optimization we should probably embrace them.

Quoting n3055:

  • An lvalue (so-called, historically, because lvalues could appear on the left-hand side of an assignment expression) designates a function or an object. [Example: If E is an expression of pointer type, then *E is an lvalue expression referring to the object or function to which E points. As another example, the result of calling a function whose return type is an lvalue reference is an lvalue.]
  • An xvalue (an “eXpiring” value) also refers to an object, usually near the end of its lifetime (so that its resources may be moved, for example). An xvalue is the result of certain kinds of expressions involving rvalue references. [Example: The result of calling a function whose return type is an rvalue reference is an xvalue.]
  • A glvalue (“generalized” lvalue) is an lvalue or an xvalue.
  • 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. [Example: The result of calling a function whose return type is not a reference is a prvalue]

The document in question is a great reference for this question, because it shows the exact changes in the standard that have happened as a result of the introduction of the new nomenclature.

Kornel Kisielewicz
+1 Excellent reference
Tyler McHenry
@Tyler, the nice thing is that it shows the changes
Kornel Kisielewicz
+29  A: 

What are these new categories of expressions?

The FCD (n3092) has an excellent description:

— An lvalue (so called, historically, because lvalues could appear on the left-hand side of an assignment expression) designates a function or an object. [ Example: If E is an expression of pointer type, then *E is an lvalue expression referring to the object or function to which E points. As another example, the result of calling a function whose return type is an lvalue reference is an lvalue. —end example ]

— An xvalue (an “eXpiring” value) also refers to an object, usually near the end of its lifetime (so that its resources may be moved, for example). An xvalue is the result of certain kinds of expressions involving rvalue references (8.3.2). [ Example: The result of calling a function whose return type is an rvalue reference is an xvalue. —end example ]

— A glvalue (“generalized” lvalue) is an lvalue or an xvalue.

— An rvalue (so called, historically, because rvalues could appear on the right-hand side of an assignment expressions) is an xvalue, a temporary object (12.2) 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. [ Example: The result of calling a function whose return type is not a reference is a prvalue. The value of a literal such as 12, 7.3e5, or true is also a prvalue. —end example ] Every expression belongs to exactly one of the fundamental classifications in this taxonomy: lvalue, xvalue, or prvalue. This property of an expression is called its value category. [ Note: The discussion of each built-in operator in Clause 5 indicates the category of the value it yields and the value categories of the operands it expects. For example, the built-in assignment operators expect that the left operand is an lvalue and that the right operand is a prvalue and yield an lvalue as the result. User-defined operators are functions, and the categories of values they expect and yield are determined by their parameter and return types. —end note

I suggest you read the entire section 3.10 Lvalues and rvalues though.

How do these new categories relate to the existing rvalue and lvalue categories?

Again:

           expression
            ╱    ╲
          ╱        ╲
       glvalue     rvalue
      ╱     ╲     ╱   ╲
    ╱         ╲ ╱       ╲
 lvalue       xvalue     prvalue

Are the rvalue and lvalue categories in C++0x the same as they are in C++03?

The semantics of rvalues has evolved particularly with the introduction of move semantics.

Why are these new categories needed?

So that move construction/assignment could be defined and supported.

dirkgently
Nice ASCII art :)
FredOverflow
Thanks to Potatoswatter and In silico who fixed it ;)
dirkgently
Unicode art ⋙ Ascii art
badp
@badp: Thanks, but it looks a bit too hairy now :P
dirkgently
google charts make this easy: chart.apis.google.com/chart?cht=gvglvalue->xvalue;expression->rvalue->xvalue;rvalue->prvalue}
Johannes Schaub - litb
+2  A: 

How do these new categories relate to the existing rvalue and lvalue categories?

A C++03 lvalue is still a C++0x lvalue, whereas a C++03 rvalue is called a prvalue in C++0x.

FredOverflow
+5  A: 

C++03's categories are too restricted to capture the introduction of rvalue references correctly into expression attributes.

With the introduction of them, it was said that an unnamed rvalue reference evaluates to an rvalue, such that overload resolution would prefer rvalue reference bindings, which would make it select move constructors over copy constructors. But it was found that this causes problems all around, for example with Dynamic Types and with qualifications.

To show this, consider

int const&& f();

int main() {
  int &&i = f(); // disgusting!
}

On pre-xvalue drafts, this was allowed, because in C++03, rvalues of non-class types are never cv-qualified. But it is intended that const applies in the rvalue-reference case, because here we do refer to objects (= memory!), and dropping const from non-class rvalues is mainly for the reason that there is no object around.

The issue for dynamic types is of similar nature. In C++03, rvalues of class type have a known dynamic type - it's the static type of that expression. Because to have it another way, you need references or dereferences, which evaluate to an lvalue. That isn't true with unnamed rvalue references, yet they can show polymorphic behavior. So to solve it,

  • unnamed rvalue references become xvalues. They can be qualified and potentially have their dynamic type different. They do, like intended, prefer rvalue references during overloading, and won't bind to non-const lvalue references.

  • What previously was an rvalue (literals, objects created by casts to non-reference types) now becomes an prvalue. They have the same preference as xvalues during overloading.

  • What previously was an lvalue stays an lvalue.

And two groupings are done to capture those that can be qualified and can have different dynamic types (glvalues) and those where overloading prefers rvalue reference binding (rvalues).

Johannes Schaub - litb
+4  A: 

I'll start with your last question:

Why are these new categories needed?

The C++ standard contains many rules that deal with the value category of an expression. Some rules make a distinction between lvalue and rvalue. For example, when it comes to overload resolution. Other rules make a distinction between glvalue and prvalue. For example, you can have a glvalue with an incomplete or abstract type but there is no prvalue with an incomplete or abstract type. Before we had this terminology the rules that actually need to distinguish between glvalue/prvalue referred to lvalue/rvalue and they were either unintentionally wrong or contained lots of explaining and exceptions to the rule a la "...unless the rvalue is due to unnamed rvalue reference...". So, it seems like a good idea to just give the concepts of glvalues and prvalues their own name.

What are these new categories of expressions? How do these new categories relate to the existing rvalue and lvalue categories?

We still have the terms lvalue and rvalue that are compatible with C++98. We just divided the rvalues into two subgroups, xvalues and prvalues, and we refer to lvalues and xvalues as glvalues. Xvalues are a new kind of value category for unnamed rvalue references. Every expression is one of these three: lvalue, xvalue, prvalue. A Venn diagram would look like this:

    ______ ______
   /      X      \
  /      / \      \
 |   l  | x |  pr  |
  \      \ /      /
   \______X______/
       gl    r

Examples with functions:

int   prvalue();
int&  lvalue();
int&& xvalue();

But also don't forget that named rvalue references are lvalues:

void foo(int&& t) {
  // t is initialized with an rvalue expression
  // but is actually an lvalue expression itself
}

cheers! s

sellibitze
+1 : l33t ASCII Art ^^
Kornel Kisielewicz