Domanda

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.

È stato utile?

Soluzione

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.

Altri suggerimenti

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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
scroll top