3

Possible Duplicate:
Undefined Behavior and Sequence Points

I'm having trouble understanding the order of actions when overloading the postfix operator. Let's examine the two small examples below:

int i = 0;
std::cout << std::endl << "i: " << i;
i = ++i;
std::cout << std::endl << "i: " << i;
i = i++;
std::cout << std::endl << "i: " << i;

MyClass myObject;
std::cout << std::endl << "myObject: " << myObject.getMyValue();
myObject = ++myObject;
std::cout << std::endl << "myObject: " << myObject.getMyValue();
myObject = myObject++;
std::cout << std::endl << "myObject: " << myObject.getMyValue();

Two very different behaviors emerge. The output is as follows:

i: 0
i: 1
i: 2
myObject: 0
myObject: 1
myObject: 1

Different behavior, you see. Here's the outline of my overloaded-operator methods.

MyClass & MyClass::operator++ ()
{
    ++myValue;
    return *this;
}

MyClass MyClass::operator++ (int postfixFlag)
{
    MyClass myTemp(*this);
    ++myValue;
    return myTemp;
}

Alright. Prefix makes sense. You increment whatever you need to, then return the same object, now modified, in case of assignment. But postfix is what's tripping me up. It's supposed to assign, then increment. Here we're self assigning. So with the built-in integer type, it makes sense. I assign i's value to itself, then i gets incremented. Fair enough. But let's say MyClass is a recreation of the int. It starts out at 0, gets prefix-incremented, and becomes 1. Then, the key line. myObject = myObject++. That's the same thing as myObject = myObject.operator++(int postfixFlag). It gets called. myTemp gets initialized with the value 1. It's incremented to 2. Then we return the temp. That works, if we're assigning to another object. But here I'm self-assigning, so after the increment to 2, myObject is set equal to the returned temp object initialized with the initial value, and we're back to 1! That makes sense. But it's a fundamentally different behavior.

How do I work around it? How does int do it? How is this method generally written? Do you have any comments about C++ behavior and design relating to this? Etc. I'm a little perplexed right now, since books and online examples always seem to use a variant on the method above.

Thanks for reading, and any input will be appreciated!

Community
  • 1
  • 1
Walker
  • 1,215
  • 2
  • 13
  • 26
  • 6
    `i = i++` and i = ++i` both exhibit undefined behavior. – James McNellis Jun 29 '11 at 04:11
  • 2
    Your `int` behaviour is undefined... you could get different results with different compilers, version, optimisation levels, target CPUs, or for no obvious reason at all... you need to do some background reading on *sequence points*... there are lots of questions here on S.O. addressing them. – Tony Delroy Jun 29 '11 at 04:12
  • 1
    There even is a [C++-FAQ](http://stackoverflow.com/questions/4176328/undefined-behavior-and-sequence-points). – Xeo Jun 29 '11 at 04:13
  • 1
    Why the close votes? Sure, there is another FAQ which explains about sequence points in general, and it is good background reading, but it is not an "exact duplicate" of this question. In fact I think it's quite hard to figure out the answer to this question just by reading a general lecture on sequence points. Hopefully my answer will suffice. I'm +1 on this question. I definitely think it deserves its own page on SO. – mgiuca Jun 29 '11 at 04:53
  • @mgiuca: Because we get some version of this question every week. Sometimes more frequently than that. – dmckee --- ex-moderator kitten Jun 30 '11 at 16:41
  • 1
    Believe it or not, I searched before posting. You call this an exact duplicate, but it echoes other questions only if one know of sequence points. My question made no mention of them because in all the books I've worked out of, none has ever discussed them. I can't find any SO questions that inquire about my issue and also lack prior knowledge of either undefined behavior or sequence points. For that reason, I stand by this and object to its closure. Remember that a concept obvious with experience may be unheard of to the uninitiated. Thank you for the links though, they were informative. – Walker Jul 17 '11 at 05:09
  • 1
    I agree with @Walker Argendeli. If you have a question "How do I do X?" and the answer is "Read up on Y," and another question "How do I Y?", those are two different questions -- you can't find the latter if you don't know about Y. Bad close votes. – mgiuca Jul 22 '11 at 01:25

1 Answers1

5

As others have said, with int the behaviour is undefined. But I thought I'd try to explain why for your MyClass it is not ever getting to 2.

The trick is that you are taking the following three steps in the postfix version:

  1. Making a copy of this called myTemp (with myValue == 1).
  2. Incrementing this->myValue (so myTemp.myValue == 1; this->myValue == 2).
  3. Returning myTemp (with myValue == 1).

So you are modifying this, but the code that calls myObject++ is never going to see this again. It's only going to look at the value returned, which is a copy of the old myObject.

The code for operator++ is fine. The problem is how you are using it -- you shouldn't be writing the result of a pre-increment or post-increment back to the same variable (behaviour is undefined). Here is some code that might be more instructive:

int i = 0;
std::cout << "i: " << i << std::endl;
int j = ++i;
std::cout << "i: " << i << ", j: " << j << std::endl;
int k = i++;
std::cout << "i: " << i << ", k: " << k << std::endl;

MyClass myObject;
std::cout << "myObject: " << myObject.getMyValue() << std::endl;
MyClass myObject1 = ++myObject;
std::cout << "myObject: " << myObject.getMyValue()
    << ", myObject1: " << myObject1.getMyValue() << std::endl;
MyClass myObject2 = myObject++;
std::cout << "myObject: " << myObject.getMyValue()
    << ", myObject2: " << myObject2.getMyValue() << std::endl;

This prints:

i: 0
i: 1, j: 1
i: 2, k: 1
myObject: 0
myObject: 1, myObject1: 1
myObject: 2, myObject2: 1

I changed your code so that rather than assigning back to itself, it assigns to a fresh variable each time. Note that in both the int and the MyClass cases, the main variable (i/myObject) is incremented both times. However, in the pre-increment case, the fresh variable (j/myObject1) takes on the new value, while in the post-increment case, the fresh variable (k/myObject2) takes on the old value.

Edit: Just answering another part of the question, "How does int do it?" I assume this question means "what does the pre-increment and post-increment code look like in the int class, and how can I make mine the same?" The answer is, there is no "int class". int is a special built-in type in C++ and the compiler treats it specially. These types aren't defined with ordinary C++ code, they are hard-coded into the compiler.

Note: For anyone who wants to try this themselves, here is the code for MyClass that the question didn't include:

class MyClass
{
private:
    int myValue;
public:
    MyClass() : myValue(0) {}
    int getMyValue() { return myValue; }
    MyClass& operator++();
    MyClass operator++(int postfixFlag);
};
mgiuca
  • 20,958
  • 7
  • 54
  • 70
  • +1 from me, because we somehow just ignored the case with `myObject` and we just leapt at the undefined behaviour. :) – Xeo Jun 29 '11 at 04:56
  • Sorry for the late best answer—I've been out of the country. Appreciate the detail – Walker Jul 17 '11 at 04:58