Stream insertion combined with postfix operators - c++

Say I was reading data in which the indexing starts at 1. I want my indexing to be 0 based.
Why is it not acceptable to use postfix increment/decrement operators after a stream insertion operator?
int a;
std::cin >> a--;

The result of a post-increment operator is a temporary object, even if the increment is strictly applied only at the end of the expression. To read a value, an lvalue is needed but temporary objects aren't lvalues: they are bound to go away. Note, that you'd also read the value into the temporary, retaining an unspecified value in a.

operator>> has an overload that takes a int&. The result of a-- is an rvalue, and you can't bind a non-const reference to an rvalue.

Related

Why can't a++ (post-increment operator) be an Lvalue?

Code
#include<iostream>
int main()
{
int a=3;
a++=5;
std::cout<<a;
}
Output (as expected)
[Error] lvalue required as left operand of assignment
1. The post increment operator (a++) has the highest priority in the table. So it will definitely execute before the assignment operator (=). And as per the rule of post increment the value of variable a will increment only after execution of that statement.
So what exactly happens when the post increment operator (++)
executes before the assignment operator (=)?
2.
In C both the pre- and post-increment operators yield rvalues but C++ updated the pre-increment operator to an lvalue while keeping the post-increment operator as an rvalue only. The reason for that is we can't make it an lvalue as it possesses only the old value, not the updated one. But I don't understand this reason properly.
See now a++ has the rvalue 3, not the variable itself, right? But what if it brings a variable which possesses an lvalue, then 5 will insert into that and after the end of the statement its value will be 6. What's the problem with this and why can't it be done?
And As per rule of post increment the value of variable a will increment only after execution of that statement.
That's a bit misleading. The variable is incremented immediately. But the result of the expression is the old value. This should make it easier to understand why it cannot be an lvalue. The modified object doesn't have the old value, so the hypothetical lvalue cannot refer to that object. The value is a new, temporary object; it is a prvalue.
As an analogy, think about writing a function that does the same thing as post increment. This is how it would be written (if you define overloaded operator overload for a class, then a function such as this is quite literally what you'd write with a few changes):
int post_increment(int& operand)
{
int old = operand;
operand += 1;
return old;
}
How could you meaningfully re-write that function to return an lvalue (reference) and still have the behaviour expected from the post increment?

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.

Chaining the ++ operator

I noticed in C++ that something like++++i is allowed and increments i by 2. However, i++++ is not allowed. Why is the latter not allowed whereas the former is?
The result of postfix ++ is a prvalue. Postfix ++ can only be applied to modifiable lvalue. So you can't apply postfix ++ to the result of another postfix ++. This makes sense because it needs some object to modify. The result of the operator is just a value (a copy of the original value of the operand) - there's no object for you to modify.
On the other hand, the result of prefix ++ is an lvalue and it also expects an lvalue as its operand. Therefore you can pass the result of a prefix ++ to another ++.
It might help for you to think about lvalues as denoting objects in memory and rvalues as just values (that may have come from an object in memory). Postfix ++ expects an lvalue because it wants an object that it can modify. It returns an rvalue because the result is just a value not associated with any object (because it was copied from the operand before modification). Prefix ++ also wants to modify its operand, so also expects an lvalue. However, it returns the object after modification, which is the operand object itself and so its result is an lvalue too.

Why does C++ accept multiple prefixes but not postfixes for a variable

While looking into Can you have a incrementor and a decrementor on the same variable in the same statement in c
I discovered that you can have several prefix increment/decrement operators on a single variable, but only one postfix
ex:
++--++foo; // valid
foo++--++; // invalid
--foo++; // invalid
Why is this?
This is due to the fact that in C++ (but not C), the result of ++x is a lValue, meaning it is assignable, and thus chain-able.
However, the result of x++ is NOT an lValue, instead it is a prValue, meaning it cannot be assigned to, and thus cannot be chained.
In C++ language prefix increment/decrement operators return lvalues, while postfix ones return rvalues. Meanwhile, all modifying operators require lvalue arguments. This means that the result of prefix increment/decrement can be passed on to any other additional operator that requires an lvalue argument (including additional increments/decrements).
For the very same reason in C++ you can write code like this
int i = 0;
int *p = &++i;
which will increment i and make p point to i. Unary & requires lvalue operand, which is why it will work with the result of prefix ++ (but not with postfix one).
Expressions with multiple built-in prefix increments/decrements applied to the same object produce undefined behavior, but they are nevertheless well-formed (i.e. "compilable").
Expressions like ++foo-- are invalid because in C++ postfix operators have higher precedence than prefix ones. Braces can change that. For example, (++foo)-- is a well-formed expression, albeit leading to undefined behavior again.

Why pre-increment operator gives rvalue in C?

In C++, pre-increment operator gives lvalue because incremented object itself is returned, not a copy.
But in C, it gives rvalue. Why?
C doesn't have references. In C++ ++i returns a reference to i (lvalue) whereas in C it returns a copy(incremented).
C99 6.5.3.1/2
The value of the operand of the prefix ++ operator is incremented. The result is the new value of the operand after incrementation. The expression ++Eis equivalent to (E+=1).
‘‘value of an expression’’ <=> rvalue
However for historical reasons I think "references not being part of C" could be a possible reason.
C99 says in the footnote (of section $6.3.2.1),
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’’. What is sometimes called
‘‘rvalue’’ is in this International
Standard described as the ‘‘value of
an expression’’.
Hope that explains why ++i in C, returns rvalue.
As for C++, I would say it depends on the object being incremented. If the object's type is some user-defined type, then it may always return lvalue. That means, you can always write i++++++++ or ++++++i if type of i is Index as defined here:
Undefined behavior and sequence points reloaded
Off the top of my head, I can't imagine any useful statements that could result from using a pre-incremented variable as an lvalue. In C++, due to the existence of operator overloading, I can. Do you have a specific example of something that you're prevented from doing in C, due to this restriction?