# Re: r- r-value vs l-value?

Zach wrote:
Chris Smith wrote:

Sure. An l-value is an expression that may legally appear on the left-
hand side of the assignment operator, =. An r-value is an expression
that may appear on the right-hand side. The set of r-values is a subset
of the the set of l-values. For example, given:

int a;
int b[50];

The following are l-values (and also r-values):

a
b[10]

Whereas the following are ONLY r-values:

17
a + 5
b

Therefore, the following line of code will fail to compile:

a + 5 = 15;

Hope that helps,

Where are the rules of what may go on each side?

Since there is a lot of confusion in that discussion, I'll give rules
(based on C++).
First, I'll correct statements of Chris Smith:
int a;
int b[50];
a is an lvalue
b[10] is an lvalue
17 is an rvalue
a+5 is an rvalue
b *is an lvalue*

An lvalue designate an object (it may designate an inexistent or
invalid object), while an rvalue is a value itself...
In some sense, an lvalue is not a value, it is an object...

lvalues are not rvalues and rvalues are not lvalues... They are totally
different concepts.

Expressions can be rvalues or lvalues and take parameters... And
specific values are expected as arguments.
That is : there are three different contexts where an expression can
appear.
1) A context expecting an rvalue.
2) A context expecting an lvalue.
3) A context accepting both an rvalue or an lvalue (in that case,
semantics are slightly different depending on the type of the
expression).
This third category mainly exists in C++.

Any expression is either an lvalue or an rvalue.
If an lvalue-expression is found in a context where an rvalue is
expected, an rvalue-to-lvalue conversion occurs (and this conversion is
not conceptual... Physical things occur... And HERE can happen
segmentation faults).

If an rvalue is found in a context where an lvalue is expected, the
program is ill-formed.

Now, we can give the rule (C++ rules without operator overloading, but
AFAIK they are the same in C, except where I specify it explicitly).

string literals are lvalues. Other literals are rvalues.
An identifier identifying a variable (automatic or static, const or
not, volatile or not), or a function (yes) is an lvalue.
Additive (+ -), multiplicative (* / %), shift (<< >>), relationals (<
<= >= >), equality (== !=), bitwise (| & ^) and logical (|| &&)
operators expect rvalues operands and yields rvalues.

Postfix increment and decrement operators expect lvalues and yield
rvalues.

Assignment operators (= += *= etc) take a left lvalue operand and
expect a right rvalue operand and yield a rvalue in C, and an lvalue in
C++ (this is an incompatibility issue between C and C++).

Unary * operator :
It expects an rvalue operand and yields an lvalue which designates the
object pointed-to by the pointer.
The type can be incomplete, as far as this expression doesn't get an
lvalue-to-rvalue conversion.
The pointer can be NULL, as far as the expression doesn't get an
lvalue-to-rvalue conversion.

Unary & operator :
Expects an lvalue operand and yields an rvalue.

Arguments of functions expect rvalues (except if the parameter is a
reference type in C++)... What means that lvalue-to-rvalues conversion
occur if arguments are lvalues.

Function calls are rvalues (except if the returned type is a reference
type in C++).

Comma operator: It doesn't expect any particular type of value (i.e. it
accepts both lvalues and rvalues with different semantics).
The left operand can be either an rvalue or an lvalue, and is
discarded. If it is an lvalue, there is *no* lvalue-to-rvalue (nor
array-to-pointer, not function-to-pointer) conversion. In particular,
if p is a NULL pointer (*p , another_valid_expression) is valid and has
a well-defined behavior.
The right operand can either be an rvalue or an lvalue.
In C++, the resulting expression is an lvalue if (and only if) the
right operand is an lvalue, and has the same value than the right
operand, or is an rvalue if (and only if) the right operand is an
rvalue.
In C, AFAIK, the left operand can be either an rvalue or an lvalue and
the value is discarded (as in C++), but the right operand is expected
to be an rvalue, and the expression yields an rvalue.

Prefix increment and decrement operators are equivalent to +=1 and -=1
Thus, in C, they expect lvalue operands and yield rvalues, while in
C++, they expect lvalue operands and yield lvalues.

The dot operator (member access) for non-static data members (in C all
members are non-static data members) can accept an rvalue or an lvalue
(even in C, this operator enters in the third category).
If the operand is an lvalue, the expression is an lvalue.
If the operand is an rvalue, the expression is an rvalue.

p[i] is equivalent to *(p + i) and thus, expect rvalue operands, and
yield an lvalue.
Similarly p->member is equivalent to (*p).member and expect an rvalue
operand and yield an lvalue.

conditional expressions (aka the ?: ternary operator) has also
different meanings in C and C++
It expects an rvalue as first operand (converted to bool in C++).
In C, it is quite simple : It expects rvalues second and third
operands, and yields an rvalue.
In C++ (with simplified rules), if both operands are of the same type
and are lvalues, the result is an lvalue.
If one operand can be implicitly converted to a reference to the other
type, the result is an lvalue.
If one operand has void data type, both operands are converted to void,
and get array-to-pointer, function-to-pointer and lvalue-to-rvalues
conversions, and the result is an rvalue (of void type).
Otherwise the result is an rvalue, and additional obfuscated rules
apply.
If you want to know all the C++ rules for conditional expressions, look
at:
http://www.open-std.org/jtc1/sc22/wg21/docs/wp/html/nov97-2/expr.html#expr.cond

Conversion expressions :
Expect an rvalue operand in C, and yields an rvalue in C.
In C++, they expect and yield an lvalue if the destination type is a
reference type.

From the C++ standard : an lvalue-to-rvalue conversion is:
"
4.1 Lvalue-to-rvalue conversion
[conv.lval]

1 An lvalue (_basic.lval_) of a non-function, non-array type T can
be
converted to an rvalue. If T is an incomplete type, a program
that
necessitates this conversion is ill-formed. If the object to
which
the lvalue refers is not an object of type T and is not an object of
a
type derived from T, or if the object is uninitialized, a program
that
necessitates this conversion has undefined behavior. If T is a
non-
class type, the type of the rvalue is the cv-unqualified version of
T.
Otherwise, the type of the rvalue is T. 1)

2 The value contained in the object indicated by the lvalue is
the
rvalue result. When an lvalue-to-rvalue conversion occurs within
the
operand of sizeof (_expr.sizeof_) the value contained in the
refer-
enced object is not accessed, since that operator does not
evaluate
its operand."

I would also like to explain where array-to-pointer conversion occurs.
Knowing that a variable whose type is an array is an lvalue (like any
other variable).

Whenever an lvalue array expression appears as an operand of an
operator that expects an rvalue for that operand, the array-to-pointer
conversion is applied to that operand.
Variadic arguments (i.e. arguments after an ellipsis) get
array-to-pointer conversions (and integer promotion for integer types).
In a conversion expression, if the result is an rvalue (i.e. the
destination type is not a reference type), then the left operand
expects an rvalue, and array-to-pointer conversion occur if the
parameter is of array type.
If the result of a conditional operator (?:) is an rvalue, then the
second and third operand are expected to be rvalues (and
array-to-pointer conversion occur).

One thing that confuse many programmers is the fact that an array can't
be assigned, can't be copy-constructed (in C++), can't be passed as
function parameter, and can't be returned from a function.
What is very confusing for programmers is that you can declare function
parameters which look like arrays but are pointers (good old pointer
automatic variables).
Old K&R C had similar limitations for structures.

If you read the C++ standard, you'll see that there are several places,
where it is explicitly stated that array-to-pointer,
function-to-pointer and lvalue-to-rvalue conversion does not occur!
It is stated in the standard in order to avoid incorrect
interpretations by programmers/compiler-vendors who tend to think that
there are such conversions where there are none.
There is *NO* array-to-pointer (nor lvalue-to-rvalue nor
function-to-pointer) conversion :
1) For the argument of typeid (in C++).
2) When a type is explicitly converted to void
3) On the operand of sizeof

From the C++ standard:
" 4.2 Array-to-pointer conversion
[conv.array]

1 An lvalue or rvalue of type "array of N T" or "array of unknown
bound
of T" can be converted to an rvalue of type "pointer to T."
The
result is a pointer to the first element of the array."

Now I hope that everybody will understand the basics of lvalues and
rvalues.

.

## Relevant Pages

• Re: Managed C++ Code Generation Bug?
... >> binds directly without an initial conversion to an rvalue, ... My mom always told me to stay out of lvalue vs. rvalue ... >I don't see why treating a static const member as an lvalue should cause ...
(microsoft.public.dotnet.languages.vc)
• Re: r- r-value vs l-value?
... The following are l-values (and also r-values): ... bis an lvalue ... a+5 is an rvalue ... Before this conversion takes place, b is an lvalue; ...
(comp.lang.c)
• Re: string literal is an lvalue; other literals are rvalues.
... >> As I understand the distinction between rvalue and lvalue, ... but not of an rvalue. ... such as the int value 5. ... There is talk of lvalue to rvalue conversion in 4.1. ...
(comp.lang.cpp)
• Re: Some explanation on *((int*)vp) = 3;
... the copy assignment operator of the class is called. ... operand must be a modifiable lvalue. ... is the unqualified type of the left hand operand. ... including lvalue to rvalue conversion. ...
(alt.comp.lang.learn.c-cpp)
• Re: r- r-value vs l-value?
... "The operand of the unary & operator shall be either a function ... designator, the result of a or unary * operator, or an lvalue that ... lvalue is that "the result" can be either an lvalue or rvalue. ...
(comp.lang.c)