often used seldom defined terms: lvalue
Asked Answered
O

8

22

What is an lvalue?

Ornithorhynchus answered 23/2, 2009 at 21:20 Comment(1)
M
30

An lvalue is a value that can be assigned to:

lvalue = rvalue;

It's short for "left value" or "lefthand value" and it's basically just the value on the left of the = sign, i.e. the value you assign something to.

As an example of what is not an lvalue (i.e rvalue only):

printf("Hello, world!\n") = 100; // WTF?

That code doesn't work because printf() (a function that returns an int) cannot be an lvalue, only an rvalue.

Modifier answered 23/2, 2009 at 21:23 Comment(7)
+1 nice! "// WTF" made me laugh! However, it would qualify as an lvalue if printf returned a reference.Cartagena
That code doesn't work because the return value of printf() is int, which is not an lvalue. You are not assigning to the function itself.Princessprinceton
@Princessprinceton - In my (admittedly brief) experience with C, I've never had a function I had to (or would even want to) assign to, but I see the answer below demonstrating this in C++, so I won't dispute that it can happen. I doubt, though, that this guy is going to have to worry about that any time soon.Modifier
I've spent an hour parsing and understanding WP's "Value (computer science)" when in fact what I really needed was to search for it on SO!..Compressibility
This is actually wrong. A value that can be assigned to is an lvalue, but not all lvalues can be assigned to.Billybillycock
"A modifiable lvalue is an lvalue that does not have array type, does not have an incomplete type, does not have a const- qualified type, and if it is a structure or union, does not have any member (including, recursively, any member or element of all contained aggregates or unions) with a const- qualified type. "Billybillycock
The corollary is that there are lvalues that fail one or more of these which cannot be assigned to.Billybillycock
H
12

Something that appears on the left hand side of an assignment i.e. something that can be assigned to.

Note that in C++ a function call may be an lvalue if:

int & func() {
   static int a = 0;
   return a;
}

then:

func() = 42;     // is legal (and not uncommon)
Hooge answered 23/2, 2009 at 21:22 Comment(3)
But strange, and potentially breaking if you are returning an object...which is why many times you'll see a const there too.Zavala
I've seen for c++1x they consider adding ref-qualifiers to the implicit operator=. that would mean that func() = 42; would not be allowed anymore for user-defined class-type rvalues (it is allowed currently)Woodman
and in any case, if you have int*func(){... return &a;} you can do: (*func()) = 42; That's worse, and I don't think they can stop you...Germano
W
12

It's traditionally the left side of the "=" operator. However, with time, meaning of "lvalue"/"rvalue" changed. C++ added the term of a "non-modifiable lvalue" which is any lvalue that cannot assigned to: arrays and variables that are qualified with "const" are two examples. In C, you cannot assign to any rvalue (see below). Likewise, in C++, you cannot assign to rvalues that are not of some user defined class type.

You can say an "lvalue" is an expression that names an object which persists over time and occupies some location of storage. Whether or not you can assign to that expression is not important for that classification. A reference, in particular, is also an lvalue, because it has a name that persists over time. All the following are lvalues, because they all refer to named objects. Also note that a const does not have any effect on the lvalue-ness.

int a; lvalue: a;
       lvalue: ++a;
int a[2]; lvalue: a;
int &ra = a; lvalue: ra;
int *pa = &a; lvalue: *pa;

The term "rvalue" is used for things like literals and enumerator values and for temporaries that do not enjoy the fun of having a long life and are destroyed right away at the end of a full expression. For rvalues, not the aspect of persistence is important, but the value-aspect. Functions in C++ are lvalues, because they are persistent and they have an address, even though they are not objects. I've left them out in the above overview of lvalues, because it's easier to grasp lvalues when first only taking objects into account. All the following are rvalues:

enum { FOO, BAR }; rvalue: BAR;
int a[2]; rvalue: (a+1);
rvalue: 42;
int a; rvalue: a++; // refering to a temporary
struct mystruct { }; mystruct f() { return mystruct(); } rvalue: f();

Incidentally, often you have an lvalue, but an operator needs an rvalue. For example the binary builtin "+" operator adds two values. An lvalue expression first and for all specifies a location where a value first has to be read out. So when you add two variables, an "lvalue to rvalue" conversion takes place. The Standard says that the value contained in an lvalue expression is its rvalue result:

int a = 0, b = 1;
int c = a + b; // read values out of the lvalues of a and b. 

Other operators do not take rvalue, but lvalues. They don't read a value. An example is the address-of operator, &. You cannot take the address of an rvalue expressions. Some rvalues are not even objects: They do not occupy any storage. Examples are again, literals (10, 3.3, ...) and enumerator values.

How is that scary stuff useful?

Well it has several advantages to have the distinction of lvalue and rvalue

  • Allowing the compiler to omit taking storage for rvalues and using registers/readonly memory for scalar values
  • Flagging expressions as elusive: rvalues will not live long
    • Allows efficient copy semantics for the compiler internally and in c++1x also exposed to the programmer (see move semantics and rvalue references): We can steal away resources from rvalues that are going to be destroyed anyway.
  • Allows to build rules upon that property
    • rvalues are not allowed to be generated from a yet uninitialized objects where an lvalues refers to. But lvalues may refer to uninitialized objects just fine
    • rvalues can never be polymorphic. Their static type must also be their dynamic type: Simplifies rules for the typeid operator.

... There is more to it, i feel it ...

Woodman answered 23/2, 2009 at 21:46 Comment(1)
+1 for mentioning that you can't take the address of a rvalue. IMO, this is the easiest way to distinguish rvalues from lvalues.Anthropography
R
8

One of the best explanations I know of can be found in this article on RValue references.

another way to determine whether an expression is an lvalue is to ask "can I take its address?". If you can, it's an lvalue. If you can't, it's an rvalue. For example, &obj , &*ptr , &ptr[index] , and &++x are all valid (even though some of those expressions are silly), while &1729 , &(x + y) , &std::string("meow") , and &x++ are all invalid. Why does this work? The address-of operator requires that its "operand shall be an lvalue" (C++03 5.3.1/2). Why does it require that? Taking the address of a persistent object is fine, but taking the address of a temporary would be extremely dangerous, because temporaries evaporate quickly.

Ratify answered 23/2, 2009 at 21:33 Comment(3)
while this is true, it's kind of a self defining statement. First it says: 'another way to determine whether an expression is an lvalue is to ask "can I take its address?"' Then it says 'Why does this work? The address-of operator requires that its "operand shall be an lvalue"' Not exactly usefulShaped
So basically it has said, "it's an lvalue if you can take its address because it can only be an lvalue if you can take its address"Shaped
It is useful in the sense that most developers already know where they can use the & operator. If it was a definition, it woold of course be flawed, but it is really a rule of thumb.Ratify
O
2

The "L" in lvalue is usually described as standing for "location". An lvalue specifies the location of something; as another answerer pointed out, lvalues can typically have their address taken. This is why numeric literals and non-reference function return values are not lvalues.

The L used to stand for "left" until const was introduced into C++. Const lvalues cannot appear on the left hand side of an assignment.

Overshoe answered 23/2, 2009 at 22:5 Comment(0)
N
1

For the C programming language C11 draft n1570 6.3.2.1p1 puts it succinctly:

An lvalue is an expression (with an object type other than void) that potentially designates an object [...]

It is as simple as that.

The Wikipedia definition on the day of writing this answer was almost as good

[...] a value that points to a storage location, potentially allowing new values to be assigned or value that provide accessible memory address of a variable where data can be written, it is referred as location value.


Examples of lvalue expressions? Given

int a, b[1], *c;

we can have the following lvalue expressions: a, b and c each of which definitely designate an object. b[0] designates an object as well, hence it is an lvalue expression. What about the potentiality then? *c is always an lvalue expression, but it does not designate an object if c does not hold a pointer to a valid object. b[1] could semantically designate an object but it doesn't because it accesses the array out of bounds. Both of them are lvalue expressions.

When an lvalue expression that does not actually designate an object is evaluated, the behaviour is undefined.

An lvalue expression can be more complex than that, for example:

((flag ? foo() : bar())->pointed_to_by_struct_member[42])[0] 

if it compiles, it is an lvalue expression.

Basically everything that you can apply the & (address-of operator) to in C is an lvalue, except functions, since functions are not objects in C.

So what does the L then stand for?

C11 n1570 Footnote 64:

64) The name lvalue comes originally from the assignment expression E1 = E2, in which the left operand E1 is required to be a (modifiable) lvalue. It is perhaps better considered as representing an object locator value. [...]


Notice that E1 = E2 is not required to compile for E1 to be an lvalue in ISO C. 6.3.2.1p1 continues:

A modifiable lvalue is an lvalue that does not have array type, does not have an incomplete type, does not have a const-qualified type, and if it is a structure or union, does not have any member (including, recursively, any member or element of all contained aggregates or unions) with a const-qualified type.

It used to be case in languages before C (for example B) that all locator values could appear on the left-hand side of the assignment, hence the naming. This is no longer the case in C since arrays could never have been assigned to, and ISO C added const.


P.S. The same footnote explains the related term rvalue like this:

[...] What is sometimes called rvalue is in this International Standard described as the value of an expression. [...]

Nigelniger answered 16/10, 2018 at 16:41 Comment(0)
S
0

From this article. Since the OP was a bit lazy in asking his question (although some people disagree, see comments), I'll be lazy as well and simply paste the entire relevant part here, probably breaking some copyright laws.

An object is a region of storage that can be examined and stored into. An lvalue is an expression that refers to such an object. An lvalue does not necessarily permit modification of the object it designates. For example, a const object is an lvalue that cannot be modified. The term modifiable lvalue is used to emphasize that the lvalue allows the designated object to be changed as well as examined. The following object types are lvalues, but not modifiable lvalues:

  • An array type
  • An incomplete type
  • A const-qualified type
  • An object is a structure or union type and one of its members has a const-qualified type

Because these lvalues are not modifiable, they cannot appear on the left side of an assignment statement.

In C++, a function call that returns a reference is an lvalue. Otherwise, a function call is an rvalue expression. In C++, every expression produces an lvalue, an rvalue, or no value.

Certain operators require lvalues for some of their operands. The table below lists these operators and additional constraints on their usage.

     Operator                          Requirement
     & (unary)                         Operand must be an lvalue.
     ++ --                             Operand must be an lvalue.
                                          This applies to both prefix
                                            and postfix forms.
     = += -= *= %= >= &= ^= |=         Left operand must be an lvalue.

For example, all assignment operators evaluate their right operand and assign that value to their left operand. The left operand must be a modifiable lvalue or a reference to a modifiable object.

The address operator (&) requires an lvalue as an operand while the increment (++) and the decrement (--) operators require a modifiable lvalue as an operand.

Sideman answered 23/2, 2009 at 21:25 Comment(8)
The OP didn't even bother to mention what sources he already consulted before asking the question here. Why should it be a problem to suggest that he first Google it?Sideman
Because stackoverflow.com/faq doesn't require, recommend, or describe Googling questions before posting them. The intent of the site is to be a first-line resource, not a backup for when Google fails.Carcajou
Don't be so fundamentalistic about it. The OP was lazy, admit it. Showing that you have done some prior research, and specifying what you don't get about lvalues would be helpful. Unless you want to turn SO into another Wikipedia? ...What's a Wikipedia?Sideman
Well, that's the point. The FAQ doesn't say you have to be other than lazy because you don't. SO is, in fact, building a wiki database of technical answers, not just being yet another tech help forum. If it were the latter, then sure, GIYF away, but it's not.Carcajou
From the faq, first item: "What kind of questions can I ask here? Programming questions, of course! As long as your question is detailed and specific [...]" It also says to "be nice", so I just wanted to say that I still like you even after downvoting me ;)Sideman
Aww, that's nice. I still like you even though I downvoted you. And yeah, that's what it says. It doesn't say "as long as you've looked for the answer elsewhere, like in Google".Carcajou
The OP's question is as detailed as it needs to be ("detailed" as distinct from "embroidered with useless verbiage to make it look better") and entirely specific.Carcajou
Ok, we'll agree to disagree then. I'll even edit out the Google part because people might take it the wrong way, although it still was the better option in his case.Sideman
S
0

A simple example of what is definitly not an lvalue:

3 = 42;
Shortwave answered 23/2, 2009 at 21:49 Comment(1)
It does suggest a (puzzling) interpretation of the Ultimate Question, though.Genista

© 2022 - 2024 — McMap. All rights reserved.