Logically, is there a reason why ++i++ can not be a valid expression?
https://softwareengineering.stackexchange.com/questions/404289
-
06-03-2021 - |
質問
I had to increment my integer twice in a loop, so I thought I would try and be clever:
for (int i = 0; !sl.isEmpty(); ++i++) { ... }
++i++
however is not an assignable expression, at least in GCC.
Is there a technical reason why this is the case, that logically, it would be impossible to implement such a syntax rule?
In my mind,
int i(0);
QTextStream(stdout) << i++; // 0
QTextStream(stdout) << ++i; // 2
i = 0;
QTextStream(stdout) << ++i++; // impossible, but it should give 1
However I assume that there are good reasons why maybe this can not be the case. Just curious.
解決
You are misinterpreting GCC's error message:
<source>:5:8: error: lvalue required as increment operand
++i++;
^
Note where the error message is pointing: the postfix++ part.
The token sequence ++i++
will, by C/C++'s operator precedence rules, be interpreted as ++(i++)
. So the postfix++ happens first.
The postfix++ operator does two things. It returns the current value of the operand and it increments the operand... in that order (conceptually). This means that, when i++
returns something, what it returns is not i
. It has no relationship to i
. It's not a variable; it's just an int
.
The ++prefix operator is defined to do two things: increment the operand it is given and return the operand... in that order (again, conceptually).
Here's the thing though: incrementing an operand requires that the operand is a variable (or a reference in C++). That is, the operand must be a thing that is assignable, that can go on the lefthand side of an assignment. Thus, such a thing is called a "lvalue".
You cannot call either increment operator on something that isn't an lvalue. It simply doesn't make sense. It's about as reasonable as wanting to do 5 += 3
; you cannot change the value of 5. You can compute 5 + 3
, but you're not changing the value of 5 or 3. You are computing a new value, which is neither 5 nor 3. Such values cannot appear on the lefthand side of an assignment; they can only appear on the righthand side. Thus, they are called "rvalues".
Postfix++ returns an rvalue. It must return an rvalue, because that is the nature of the operation. Remember: by the time postfix++ is done, the lvalue has been changed. But the result of the expression is the previous value. Therefore, that value must be a new value, divorced from the variable that was used to generate it. There is no variable which holds this value; therefore, what postfix++ generates is an rvalue.
And you cannot increment an rvalue expression. This is why ++5
does not work. And ++(i++)
does not work for the same reason.
This is all necessary by the nature of what postfix++ means. The only way to change this would be to make postfix++ return an lvalue. But the only lvalue it has is its operand. And by the time an expression that consumes postfix++ sees the resulting lvalue, that variable will have been changed.
And that would make postfix++ behave exactly like ++prefix. So there would be no reason to have both.
There's no way around it. If you want postfix++ to actually do what postfix++ does, then ++i++
must be nonsensical code.
Now, could you redefine the meaning of assignment-like operations, so that you could make 5 += 3
simply become the equivalent of 5 + 3
? That is, assignment to rvalues simply computes a new value (5 = 3
evaluates to the expression 3
). In theory, yes.
However, if a user writes something like ++(i++)
, you need to ask yourself a question: what did they intend to have happen?
If I pretend not to know much about C++, when I see that expression, I would expect the value of i
to be incremented twice, and that the result of the expression will be the current value of i
. But that's not what would happen under those rules. The i++
part would return the original value of i
(which again, is why postfix++ exists to begin with). So the ++prefix will increment this value, resulting in i
only being incremented once. And the result would be the original value incremented once.
Similarly, 5 += 3
becoming 5 + 3
seems weird. That looks like a case of the compiler saying, "What you said doesn't make sense, so I'm going to guess at what you meant to say." And that's always a dangerous road to walk.
他のヒント
Although far from advisable (at least under normal circumstances), it's certainly possible to use operator overloading to get a compiler to accept an expression like ++i++
in C++ (getting a sane code reviewer to accept it is quite another story).
#include <iostream>
class DontDoThis {
int v;
public:
DontDoThis(int init) : v(init) {}
DontDoThis &operator++() { ++v; return *this; }
DontDoThis &operator++(int) { ++v; return *this; }
friend std::ostream &operator<<(std::ostream &os, DontDoThis const &d) {
return os << d.v;
}
};
int main() {
DontDoThis v = 2;
std::cout << ++v++;
}
In this case I've overloaded it so pre-increment and post-increment both do exactly the same thing, but you could have them act differently from each other--though there's room for substantial question about what behavior for this expression really makes sense. I tend to agree with @Nicol that this is just a bad idea in general, and we're almost certainly better off without it.