10

This question is great because it has answers on what are the differences between rvalue, lvalue, xvalue, glvalue, and prvalue. The top answer has a decent explanation of each, and I understand their differences when I read them. However, I'm having a hard time keeping them straight in my head, and I have to google them every time someone uses these terms to remind myself which one is which.

Are there any common mnemonics or other ways people remember which one is which? I'd really like to be able to keep them all straight in my head without mixing them up as I currently do.

Community
  • 1
  • 1
Cornstalks
  • 37,137
  • 18
  • 79
  • 144
  • From your linked question, I think the one line answer `A C++03 lvalue is still a C++0x lvalue, whereas a C++03 rvalue is called a prvalue in C++0x.` is the easiest way to think about it. – Jesse Good Dec 18 '12 at 21:09
  • 1
    @Close voters: where would be a more appropriate place to ask this? – Cornstalks Dec 18 '12 at 23:05
  • 1
    @Cornstalks: [programmers.se](http://programmers.stackexchange.com/)? – Mac Dec 18 '12 at 23:47

3 Answers3

9

I make 2 associations:

  • you can only take the address of an lvalue
  • xvalues is what an std::move expression is

If it's neither then it's probably a prvalue.

Now, to remember glvalues and rvalues I simply imagine this graph:

lvalues and xvalues are both glvalues, while xvalues and prvalues are both rvalues. That's really all you need to know in practice.

Maybe a mnemonic to remember lvaluse, xvalues, and prvalues go at the bottom are the words Lower eXPRessions (it contains L, X, and PR). Maybe not.

Pubby
  • 51,882
  • 13
  • 139
  • 180
3

You usually only need to know whether something is an lvalue or an rvalue. These two categories of expressions are distinct from one another.

It is usually the case that an lvalue expression refers to something that is going to hang around for a while. For example, a variable name is an lvalue because variables stick around. On the other hand, rvalue expressions usually refer to something that is only existing temporarily. You can only take the address of an lvalue because why on earth would you need the address of something that was going to disappear soon?

Now, when making this distinction, you have to take into account what the compiler knows at the time. For example, take the function void foo(const A& a). Inside this function, the compiler doesn't know if the object passed was a temporary object or not, so it just assumes that it will stick around. That is, a is an lvalue. Even though you can happily pass a temporary object into this function (call it with an rvalue expression), the expression a is still an lvalue.

Taking this into account, you can further remember whether something is an lvalue or an rvalue by whether it is named. In the above example, since the object gets named as a, it is an lvalue.

Now, for the rest of the categories, you should remember the following diagram:

Diagram of expression categories

Note that lvalues and rvalues are indeed distinct. However, rvalues are split up into two other categories, xvalues and prvalues. Also, xvalues are also considered to be glvalues along with all the lvalue expressions.

Now knowing when these show up comes down to remembering a few cases.

xvalues are not very common. They only show up in situations involving conversions to rvalue references. Here are all of the cases:

  • result of a function that returns an rvalue reference
  • a cast to an rvalue refernece
  • object.x if object is an xvalue
  • object.*member if pobject is an xvalue

By definition, prvalues are every other type of rvalue that is not an xvalue. For example, returning an object by value from a function creates a temporary returned object. The expression denoting that object is a prvalue.

The category glvalue is used to refer to everything that is not an prvalue.

Joseph Mansfield
  • 108,238
  • 20
  • 242
  • 324
0

An lvalue can be thought of as a "named" variable or memory location ... an rvalue can be thought of as an "unamed" memory location (i.e., a temporary variable returned from a function) or a pure "value" (i.e., like a number).

Jason
  • 31,834
  • 7
  • 59
  • 78
  • Not all lvalues have names, at least not anything I would describe as a name. – Ben Voigt Dec 18 '12 at 18:44
  • Just curious, can you give a quick example? I believe all lvalues have to have some type of "memory location" that can be accessed via a name. – Jason Dec 18 '12 at 18:49
  • 1
    Assuming `std::map m;`, what is the name of `m["test"]`? – Ben Voigt Dec 18 '12 at 18:50
  • Okay, I see what you're saying, but the way I see that is you're again accessing that memory location via a "name", in this case you're indexing via an `operator[]` on `m`. For instance, what is the "name" of the return value of `int foo() { return 5; }`? ... That is an example of "unnamed". But in your case you're accessing a memory location via a named object instance, thus making it an lvalue. – Jason Dec 18 '12 at 18:51
  • If you define a name that broadly, wouldn't many rvalues also have names? – Ben Voigt Dec 18 '12 at 18:54
  • Not really ... rvalues, at least in the case of rvalues that you have not declared as-such through move-semantics, don't have named memory locations. You can't directly access them, although you could "catch" them via r-value references. But once you have an r-value reference, then because that reference is named, it is treated like an lvalue (i.e., you have to explicitly move or forward it if you want to maintain its rvalue-ness). – Jason Dec 18 '12 at 19:02
  • So with `std::vector v(10);`, `v.begin()` doesn't have a name, but `*v.begin()` does? – Ben Voigt Dec 18 '12 at 20:20
  • Fine, you got me :-) ... Yes, my dilution into those two categories is a bit simplistic for the nuances of iterator operations. Of course `v.begin()` doesn't really return an object from the vector, but rather a "pointer" (i.e., iterator) to it. On the other hand `*v.begin()` does actually return the object in the vector. So I guess this is an example of where you could say an rvalue had a "name" when using my simplistic rules, breaking the system, but suffice to say, the simplicity of the idea works in most operations. – Jason Dec 18 '12 at 23:31