1

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.

5

2 Answers 2

16

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.

4
  • Look at DWIM for the last paragraph. Jan 26, 2020 at 17:23
  • 3
    There is another way to make ++i++ valid: Change the operator precedence so that it parses as (++i)++. To be honest, it would still not pass any code review done by me. Jan 26, 2020 at 18:17
  • 1
    @BartvanIngenSchenau The parentheses ensure the precedence but do not introduce a sequence before constraint. According to 6.8.1/10 this is UB, parenthesis or not.
    – Christophe
    Jan 27, 2020 at 22:18
  • 1
    @Christophe, you have a point there. I was just thinking syntactically. Jan 28, 2020 at 7:24
3

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.