2

While not the actual standard, I am relying this page on cppreference.com for this specific verbiage:

An lvalue is an expression that identifies a non-temporary object or a non-member function.

The following expressions are lvalues:

  • The name of a variable or function in scope, regardless of type, such as std::cin or std::endl. Even if the variable's type is rvalue reference, the expression consisting of its name is an lvalue expression.

...

My (simplified) comprehension of the quoted section above is that an lvalue:

  1. Is a reference
  2. Must not be a temporary object

I know that references are not objects, so point #2 must mean that in terms of a reference, it must not refer to a temporary object. However, from the expression itself, does that mean a reference to a temporary is not an lvalue? You can have references to temporary and non-temporary objects:

int myvar = 0;
int& ref_myvar = myvar; // Reference to non-temporary

class foo {};
foo const& ref_foo = foo{}; // Reference to temporary

In the above code snippet, usage of ref_foo in a separate expression later would be an lvalue or not? There is a rule for rvalue references that states that rvalue references used by name in an expression are still lvalues (because you refer to the name at that point). Does this rule also apply to lvalue references (since they also have names and using the name itself in an expression would make it an lvalue, as it does for rvalue reference variables)?

I hope I'm making some sort of sense. At least I hope the source of my confusion is evident. Some examples of how the lvalue references above would be used in an expression to prove some points relevant to my question would be a huge help as well.

void.pointer
  • 24,859
  • 31
  • 132
  • 243
  • An lvalue is not necessarily a reference, as reference is part of the type, which is orthogonal to value category. In your example, `myvar`, `ref_myvar`, and `ref_foo` are all lvalues when used as an expression. `foo{}` and `0` are not lvalues. – chris Aug 17 '15 at 16:10
  • I wrote an answer to a related question [here](http://stackoverflow.com/a/27722387/4326278), maybe it will help. – bogdan Aug 17 '15 at 20:57

1 Answers1

3

I'd say that the cppreference wording is OK for a "general introduction"-level discussion or even for "most everyday uses"-level discussion. However, once you get into the fine technical details, such statements can become somewhat misleading.

The important point is that the value category ("being an lvalue") is the property of an expression, not of an object. You can have a temporary object accessed through an lvalue, and you can have a non-temporary object accessed through an rvalue.

To refer to your examples:

ref_myvar and ref_foo are both lvalues, and always will be, regardless of how you use them. In the following:

foo&& rref = foo{};

rref is, and always will be, an lvalue as well. It is a reference to an rvalue, but the reference itself has a name and so is an lvalue.

If you want to treat an lvalue as an rvalue, you use the standard-provided case operator for that:

rvalue = std::move(lvalue);

Let's analyse this code:

int someint = std::move(ref_myvar);

ref_myvar is an lvalue. std::move(ref_myvar) is an rvalue. someint is an lvalue.


I don't think there is a concise way to define an lvalue without going full standardese, but name (or absence thereof) plays an important part in most definitions. I'll try my hand at such a definition; these are lvalues:

  • An expression which is a name, except for enumerators and member functions.
  • An expression of type "lvalue reference to something."
  • The result of dereferencing a pointer.

Notice that ref_myvar, ref_foo, and rref are all lvalues because they have a name. std::move(ref_myvar) doesn't have a name, and so it's an rvalue.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • 1
    To get even pickier, I would say that `rref` is an lvalue *after* `foo&& rref = foo {};`, but not *in* that statement. Within the declaration it is a declarator and not an expression at all. This also relates to the distinction that the declarator `rref` has type "rvalue reference to `foo`" but the expression `rref` (if it occurs) is an lvalue with type `foo`. – aschepler Aug 17 '15 at 16:25
  • @aschepler Yes, I meant "when used," not "in the declaration." – Angew is no longer proud of SO Aug 17 '15 at 16:27
  • @aschepler Are you saying that for declarations, the left part of the assignment is not part of the expression? How can I understand that correctly? – void.pointer Aug 17 '15 at 16:27
  • @void.pointer A declaration is not an expression (and certainly not an assignment), it's a statement. The initialiser (if any) is an expression. – Angew is no longer proud of SO Aug 17 '15 at 16:28
  • But if it were `foo f = foo{};` that's where things get confusing. The copy initialization here requires 2 special members, which IMHO are "computations" and fit the definition of an expression. How do you make the distinction in this case? – void.pointer Aug 17 '15 at 16:34
  • `foo{}` is an expression, and is used as an initialiser in the copy-initialisation of `f` in its declaration. How initialisers are used to initialise objects being declared is described in a dedicated section of the standard (8.5), and it does not constitute an expression (chapter 5). – Angew is no longer proud of SO Aug 17 '15 at 16:36
  • Cppreference now doesn't bring up temporary object in that description (i changed it to match the identity/canmove classification from http://www.stroustrup.com/terminology.pdf – Cubbi Aug 17 '15 at 17:22
  • `foo&& rref = foo{};` -- note that `decltype(rref)` is `foo&&`, an rvalue reference, despite it being an lvalue. ;) really, the entire set of (5 I think?) different value categories are required to fully explain things: rvalue and lvalue where ok for C, but C++11 ends up getting more complex. – Yakk - Adam Nevraumont Aug 17 '15 at 17:44
  • @Cubbi Very nice update to the cppreference page; the summary at the top is the best I've seen so far. Just a note: some details, mostly related to xvalues, have changed between C++11 and 14. The ones that I know of are mentioned in [this answer](http://stackoverflow.com/a/27722387/4326278) (towards the end). – bogdan Aug 17 '15 at 21:08
  • So basically references (lvalue or rvalue) only matter when looked at after the result of the expression, instead of what a name's type is? Since rvalue references returned by functions are xvalues, in that case, distinction between rvalue and rvalue reference changes its category (i.e. the return type) – void.pointer Aug 17 '15 at 21:19
  • 1
    @void.pointer Pretty much. Also, don't forget that *rvalue* and *xvalue* are not sibling terms. The lowest tier (mutually exclusive) are *lvalue,* *xvalue,* and *prvalue.* Above them, there's *rvalue* (*xvalue* or *prvalue*) and *glvalue* (*lvalue* or *xvalue*). And above those two, it's just *value.* So if a function returns an lvalue reference, it returns an lvalue. If it returns an rvalue reference or a non-reference, it returns an rvalue (which is an xvalue or a prvalue respectively). – Angew is no longer proud of SO Aug 18 '15 at 06:19
  • Another thing: Determining value category also requires *checking member function return types* as well as *checking for the existence of move assign/construct*? Is this true? It seems that having or not having move members impacts its category as an object used *by-name* in an expression. Am I getting the hang of this? – void.pointer Aug 18 '15 at 13:54
  • @void.pointer Presence of a move ctor/assignment does not impact the value category of an expression; can you explain how you think it could? And for "overloaded operator return types," sure, you need to check these. But that comes without question for me. To be able to tell the value category of an expression, you need to know what that expression is. This includes knowing whether it's a built-in operator or a function call (which overloaded operators are). – Angew is no longer proud of SO Aug 18 '15 at 13:57
  • @Angew In Stroustroup's Terminology explanation, he creates 5 combinations of 'identity' and 'movable'. At first I thought 'movable' was a reference to *semantics*, but I think he's simply referring to syntax: rvalue reference (`type&&`). – void.pointer Aug 18 '15 at 14:09