C++ lvalues and rvalues - c++

Ok, so I've been learning about lvalues and rvalues - could somebody well versed in C/C++ tell me if my thinking is correct?
Consider the code:
int i = 3;
int j = i;
Here we have two lvalues i and j, when i is assigned to j it becomes an implicit rvalue because a copy of i is stored in j. If however the code looked like:
int i = 3;
int j = &i;
Would both j and &i be lvalues because they are both physical addresses in memory? The way I understand it is that rvalues are temporary data, whereas lvalues have a physical/referenceable memory address.
Any clarification on this would be great!

The variable i never stops being an lvalue. If you can apply the address-of operator to something (like you do in the second example) then it's an lvalue.
Also in the second example &i is a pointer to i, and has the type int*. You have a type mismatch there between the declaration of j and the value you want to initialize it with.
Lastly, while i by itself is a lvalue the expression &i is not an lvalue, because you can't apply the address-of operator to it (i.e you can't do &&i).

Here we have two lvalues i and j
Yep
when i is assigned to j it becomes an implicit rvalue because a copy of i is stored in j
Not really, i is still an lvalue, but it's converted to an rvalue to read its value. This is called an lvalue-to-rvalue conversion. j is still an lvalue.
Would both j and &i be lvalues because they are both physical addresses in memory?
j is an lvalue, but &i is an rvalue; specifically a prvalue of type int*.

Being an lvalue or an rvalue is a property of an expression. Generally, all expressions which constitute a non-const qualified identifier are modifiable lvalues:
int i = 5;
i; // the expression "i" is an lvalue and is modifiable
const int j = 3;
j; // the expression "j" is still an lvalue, but not modifiable
An lvalue expression (unless qualified with const) can be modified through any of the assignment, ++ or -- operators. You can also apply the & "address-of" operator on such an expression.
Some operators like the unary *, the array subscript operator [], and the . and -> operators also yield lvalues. This means that you can say, for example, *p = 3, or a[i] = 5, or b.c++ or &c->d.
On the other hand, expressions which are not lvalues are rvalues (they are temporary and cannot be modified). If you write something like 3 = 5 or 7++, the compiler will complain that the 3 and 7 subexpressions are not lvalues.

Related

Why is "++i++" invalid while (++i)++ is valid?

Let's consider the following code:
int main() {
int i = 2;
int b = ++i++;
return 3;
}
It compiles with the following with an error:
<source>: In function 'int main()':
<source>:3:16: error: lvalue required as increment operand
3 | int b = ++i++;
| ^~
This sounds fair to me. Postfix increment has higher priority than prefix increment, so the code is parsed as int b = ++(i++); and i is an rvalue. Hence the error.
Let's now consider this variant with parenthesis to override default priorities:
int main() {
int i = 2;
int b = (++i)++;
return 3;
}
This code compiles and returns 3. On its own, this sounds fair to me but it seems in contradiction with the first code.
The question: why (++i) is an lvalue when i is not?
Thanks!
UPDATE: the error message shown above was from gcc (x86-64 9.2). Here is the exact rendering:
error with gcc
Clang x86-64 9.0.0 has a quite different message:
error with clang
<source>:3:13: error: expression is not assignable
int b = ++i++;
^ ~~~
With GCC, you get the impression that the problem is with the postfix operator and you can then wander why ++i is OK while i is not, hence my question. With Clang it is clearer that the problem is with the prefix operator.
i and ++i are both lvalues, but i++ is an rvalue.
++(i++) cannot be valid, as the prefix ++ is being applied to i++, which is an rvalue. But (++i)++ is fine because ++i is an lvalue.
Note that in C, the situation is different; i++ and ++i are both rvalues. (This is an example of why people should stop assuming that C and C++ have the same rules. People insert these assumptions into their questions, which must then be refuted.)
This declaration
int b = ++i++;
is equivalent to
int b = ++( i++ );
The postfix increment operator returns the value of the operand before increment.
From the C++ 17 Standard (8.2.6 Increment and decrement)
1 The value of a postfix ++ expression is the value of its
operand...The result is a prvalue.
While the unary increment operator returns lvalue after its increment. So this declaration
int b = (++i)++;
is valid. You could for example write
int b = (++++++++i)++;
From the C++ 17 Standard (8.3.2 Increment and decrement)
1 The operand of prefix ++ is modified by adding 1. The operand shall
be a modifiable lvalue. The type of the operand shall be an arithmetic
type other than cv bool, or a pointer to a completely-defined object
type. The result is the updated operand; it is an lvalue, and it is a
bit-field if the operand is a bit-field....
Pay attention that in C the both operators return a value instead of lvalue. So in C this declaration
int b = (++i)++;
is invalid.
so the code is parsed as int b = ++(i++); and i is an rvalue.
No. i is not an rvalue. i is an lvalue. i++ is an rvalue (prvalue to be specific).

How is a rvalue dereferenced?

The prefix operators return the object
itself as an lvalue. The postfix operators return a copy of the object’s original value
as an rvalue.
so in a statement like so *a++ a is being incremented and a copy of the original value of a is returned as rvalue but from the microsoft c++ language reference on Lvalues and Rvalues
An rvalue is a temporary value that does not persist beyond the expression that uses it
and gives an example
// lvalues_and_rvalues1.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
int main()
{
int x = 3 + 4;
cout << x << endl;
}
In this example, x is an lvalue because it persists beyond the expression that defines it. The expression 3 + 4 is an rvalue because it evaluates to a temporary value that does not persist beyond the expression that defines it.
My questions:
1) what is the rvalue being returned from the *a++ so that it can be dereferenced?
2) Did i misunderstand any concept ?
Thanks in advance!
The prefix operators return the object itself as an lvalue. The postfix operators return a copy of the object’s original value as an rvalue.
Wrong! Well, mostly. If the quote is talking about all prefix/suffix operators, then it's completely errated. However, if it's talking about the ++ and -- prefix/postfix pairs, then it's correct.
Now, taking that into account...
what is the rvalue returning from the *a++ so that it can be dereferenced?
Assuming a is a pointer of some kind, a++ increments a and yields a rvalue consisting of a's value before the increment. The increment and decrement operators, ++ and --, in both postfix and prefix forms, require an lvalue as their operator. This is because rvalues are temporary, that is, their scope is limited by the expression their occur in, so these operators make little or no sense on them. Remember, these operators not only inspect/read, but change/write to the variable itself.
The unary * operator takes a pointer(-like) object and dereferences it, yielding an lvalue found in there. It works for both rvalue and lvalue pointers. This is because * can be considered sort of a "passive" operator. It does not change/write to the pointer itself, but dereferences it and returns the lvalue object at the address stored by the pointer, whose address is of course that contained by the pointer. As all that * needs is the memory address contained in a pointer object, and the address of the pointer itself, if it has one at all, is useless here, * makes sense for both rvalues and lvalues.
You can think that * "requires an rvalue", and that "lvalues can be used as rvalues when necessary", if it clarifies (or confuses?) things a little bit more.
*a++ is equivalent to:
auto temp = *a;
a++;
// Use the value of temp here
except you can only refer to the value once, where as temp you could refer to multiple times.

Indifferent behavior of C++ code snippet

Consider the following code snippet
int a,i;
a = 5;
(i++) = a;
(++i) = a;
cout<<i<<endl;
Line (++i) = a is compiling properly and giving 5 as output.
But (i++) = a is giving compilation error error: lvalue required as left operand of assignment.
I am not able to find the reason for such indifferent behavior. I would be grateful if someone explains this.
The expression i++ evaluates to the value of i prior to the increment operation. That value is a temporary (which is an rvalue) and you cannot assign to it.
++i works because that expression evaluates to i after it has been incremented, and i can be assigned to (it's an lvalue).
More on lvalues and rvalues on Wikipedia.
According to the C++ standard, prefix ++ is an lvalue (which
is different than C), post-fix no. More generally, C++ takes
the point of view that anything which changes an lvalue
parameter, and has as its value the value of that parameter,
results in an lvalue. So ++ i is an lvalue (since the
resulting value is the new value of i), but i ++ is not
(since the resulting value is not the new value, but the old).
All of this, of course, for the built-in ++ operators. If you
overload, it depends on the signatures of your overloads (but
a correctly designed overloaded ++ will behave like the
built-in ones).
Of course, neither (++ i) = a; nor (i ++) = a; in your
example are legal; both use the value of an uninitialized
variable (i), which is undefined behavior, and both modify i
twice without an intervening sequence point.

What happens when a casted pointer has an increment operator?

For example:
int x[100];
void *p;
x[0] = 0x12345678;
x[1] = 0xfacecafe;
x[3] = 0xdeadbeef;
p = x;
((int *) p) ++ ;
printf("The value = 0x%08x", *(int*)p);
Compiling the above generates an lvalue required error on the line with the ++ operator.
The cast creates a temporary pointer of type int *. You can't increment a temporary as it doesn't denote a place to store the result.
In C and C++ standardese, (int *)p is an rvalue, which roughly means an expression that can only occur on the right-hand side of an assignment.
p on the other hand is an lvalue, which means it can validly appear on the left-hand side of an assignment. Only lvalues can be incremented.
The expression ((int *) p) treats the pointer stored inside the variable p is a pointer to int. If you want to treat the variable itself as a pointer to int variable (and then increment it), use a reference cast:
((int *&) p) ++ ;
Thanks to larsmans for pointing to the right direction.
I took the liberty of digging deeper into this. So for future reference, according to sections 6.5.2.4 and 6.5.4 of the C99 standard (http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf):
6.5.2.4 Postfix increment and decrement operators
Constraints
The operand of the postfix increment
or decrement operator shall have
qualified or unqualified real or
pointer type and shall be a modifiable
lvalue....
6.5.4 Cast operators
..
..
[Footnote] 89) A cast
does not yield an lvalue. Thus, a cast
to a qualified type has the same
effect as a cast to the unqualified
version of the type.
Note: This only applies to C. C++ may handle casts differently.
You can get the intended result with
p = (int*)p + 1;
Using the increment operator on a dereferenced pointer to p, which is an lvalue, also works:
(*(int**)&p)++;
However, the latter is not portable, since (void*)p might not have the same representation as (int*)p.
Rvalue expression ((int *) p) creates and temporary of type int* on which operator ++ cannot be applied.
++ requires an lvalue as its operand.
As #FredOverflow mentions lvalues and rvalues have very little to do with assignment.
Arrays are lvalues still they cannot be assigned to because they are non-modifiable.
std::string("Prasoon") is an rvalue expression still it can occur on the left side of assignment operator because we are allowed to call member functions( operator = in this case) on temporaries.

Example of expression evaluating to an lvalue

Is there any expression that can be evaluated to give an lvalue? I believe expressions yield an rvalue which can be assigned to an lvalue.
If you have x[y] = z;, x[y] is the lvalue and z is the rvalue. Clearly x[y] is an expression, and it can be evaluated. Therefore it can be evaluated to give an lvalue.
Gabe is correct, but more specifically, any expression that results in a reference is an lvalue. But that's not all, pointer deferences and array access are both expressions that result in an lvalue.
Another example would be a function that returns a reference, i.e. std::vector's index operator (reference operator[]( size_type n) so you can do something like this:
std::vector< Z > vector;
vector[ y ] = z;
Yet another example:
(which ? x : y) = 10;