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).
Related
Code C++17
#include <iostream>
int main()
{
bool n=-1;
n++; // Not compiles
n=n+3; // This compiles
return 0;
}
Output
ISO C++17 does not allow incrementing expression of type bool
So I am not understanding the significance of allowing addition but not increment.
As you can see in section 5.3.2 of standard draft N4296, this capability has been deprecated
The operand of prefix ++ is modified by adding 1, or set to true if it is bool (this use is deprecated)
Please note that the expression n=n+3; is not a unary statement, and it's not something that we could call deprecated. If we call it deprecated, the first problem is that there will be no implicit conversion from bool to int, for example. Since they don't know what you want to do with a not unary statement, it is reasonable that the below code gives you the output 2 for i(Deprecating this is not acceptable)
bool b = true;
int i = 1;
i = i + b;
In your example, what happens is implicit conversion from bool->int->bool
bool n=-1;
n++; // It's a unary statement(so based on the draft, it would not compile)
n=n+3; // This compiles (type(n+3) is now int, and then int is converted to bool)
Why unary increment is deprecated?
I use Galik's comment for completing this answer:
With ++b if you promote the bool b to int you end up with an r-value temporary. You can't increment r-values. So in order for ++b to have ever worked it must have been a bool operation, not a promotion to an arithmetic value. Now, that bool operation has been banned. But promotions remain legal so arithmetic that uses promoted values is fine.
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.
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.
test.(c/cpp)
#include <stdio.h>
int main(int argc, char** argv)
{
int a = 0, b = 0;
printf("a = %d, b = %d\n", a, b);
b = (++a)--;
printf("a = %d, b = %d\n", a, b);
return 0;
}
If I save the above as a .cpp file, it compiles and outputs this upon execution:
a = 0, b = 0
a = 0, b = 1
However, if I save it as a .c file, I get the following error:
test.c:7:12: error: lvalue required as decrement operator.
Shouldn't the (++a) operation be resolved before the (newValue)-- operation? Does anyone have any insight on this?
In C the result of the prefix and postfix increment/decrement operators is not an lvalue.
In C++ the result of the postfix increment/decrement operator is also not an lvalue but the result of the prefix increment/decrement operator is an lvalue.
Now doing something like (++a)-- in C++ is undefined behavior because you are modifying an object value twice between two sequence points.
EDIT: following up on #bames53 comment. It is undefined behavior in C++98/C++03 but the changes in C++11 on the idea of sequence points now makes this expression defined.
In C and C++, there are lvalue expressions which may be used on the left-hand side of the = operator and rvalue expressions which may not. C++ allows more things to be lvalues because it supports reference semantics.
++ a = 3; /* makes sense in C++ but not in C. */
The increment and decrement operators are similar to assignment, since they modify their argument.
In C++03, (++a)-- would cause undefined behavior because two operations which are not sequenced with respect to each other are modifying the same variable. (Even though one is "pre" and one is "post", they are unsequenced because there is no ,, &&, ?, or such.)
In C++11, the expression now does what you would expect. But C11 does not change any such rules, it's a syntax error.
For anybody who might want the precise details of the differences as they're stated in the standards, C99, §6.5.3/2 says:
The value of the operand of the prefix ++ operator is incremented. The result is the new
value of the operand after incrementation.
By contrast, C++11, §5.3.2/1 says:
The result is the updated operand; it is an lvalue, and it is a bit-field if
the operand is a bit-field.
[emphasis added, in both cases]
Also note that although (++a)-- gives undefined behavior (at least in C++03) when a is an int, if a is some user-defined type, so you're using your own overloads of ++ and --, the behavior will be defined -- in such a case, you're getting the equivalent of:
a.operator++().operator--(0);
Since each operator results in a function call (which can't overlap) you actually do have sequence points to force defined behavior (note that I'm not recommending its use, only noting that the behavior is actually defined in this case).
§5.2.7 Increment and decrement:
The value of a postfix ++ expression is the value of its operand. [ ... ] The operand shall be a modifiable lvalue.
The error you get in your C compilation helps to suggest that this is only a feature present in C++.
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.