3

I'm learning c++, and recently run into a confusing problem, here's the code:

#include <iostream>

using namespace std;

class A {
    public:
        A() { a[0] = 1; a[1] = 0; }
        int a[2];
        int b(void) { int x=a[0];a[0]=a[1];a[1]=x; return x; }
};

int main(void) {
    A a;
    cout<<a.a[0]<<a.a[1]<<endl; //outputs 10
    a.b();
    cout<<a.a[0]<<a.a[1]<<endl; //outputs 01
    a.b();
    cout<<a.a[0]<<a.a[1]<<endl; //outputs 10

    cout << a.b() << //outputs 1
    endl<< a.a[0]<<a.a[1] << endl; //outputs 10???

    cout<<a.a[0]<<a.a[1]<<endl; //outputs 01???
    return 0;
}

The first two calls of b() behaves as expected, but when i call b() within the cout statement, it doesn't switch the two elements of the array right away, but later i check it, it's already switched.

Can you help me understand this behavior? Thank you.

Yuushi
  • 25,132
  • 7
  • 63
  • 81
focusHard
  • 271
  • 1
  • 5
  • 12
  • 1
    Allow me to suggest that yours is a duplicate of this question (and many similar ones): http://stackoverflow.com/questions/10782863/what-is-the-correct-answer-for-cout-c-c – jogojapan Jun 06 '13 at 01:59
  • @jogojapan - it's not a duplicate of that question, because there's a function call that introduces a sequence point. – Pete Becker Jun 06 '13 at 02:06
  • @PeteBecker As you say in your answer, the two evaluations are unsequenced (relative to each other). Since one of the two has a side effect on the lvalue read by the other, it is the same basic situation as the one described in the duplicate question. – jogojapan Jun 06 '13 at 02:07
  • @jogojapan - they're not unsequenced. They're **indeterminately sequenced**. – Pete Becker Jun 06 '13 at 02:10
  • @PeteBecker There is a note in 1.9/15 C++11, in the context of argument evaluations of function calls: _Value computations and side effects associated with different argument expressions are unsequenced_. It says _unsequenced_ there. – jogojapan Jun 06 '13 at 02:12
  • Notes aren't normative. And, in particular, if they contradict words in the standard they're simply wrong. "Evaluations A and B are indeterminately sequenced when either A is sequenced before B or B is sequenced before A, but it is unspecified which." Bottom line, the rules for single-threaded program **did not change** with C++11, and in the absence of threads, the old "sequence point" rules still work. – Pete Becker Jun 06 '13 at 02:15
  • @PeteBecker Hmmmm... 1.9/13 says: _If A is not sequenced before B and B is not sequenced before A, then A and B are unsequenced._ Doesn't that apply, too? And that's not a note. – jogojapan Jun 06 '13 at 02:24
  • @jogojapan - if that was right, then `f(new int, new int)` would have undefined behavior. I think most folks would be surprised by that. But that area is certainly messy. – Pete Becker Jun 06 '13 at 02:27
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/31293/discussion-between-jogojapan-and-pete-becker) – jogojapan Jun 06 '13 at 02:28

2 Answers2

4

std::cout << f() << g();

The order of evaluation of the two function calls is unspecified; the compiler can call g() then f(), or it can call f() then g().

Same thing in your code; the compiler can squirrel away the value of a.a[0] the call a.b(), or it can call a.b() then grab the value of a.a[0].

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
  • Unfortunately the other answer has now been deleted, but I was going to your comment there: The reason why it is actually undefined behaviour is that one of the two evaluations has side effects on the lvalue the other evaluation is reading. The Standard says that this kind of situation must always be regarded as undefined behaviour. – jogojapan Jun 06 '13 at 02:06
  • @jogojapan - reference, please? – Pete Becker Jun 06 '13 at 02:07
  • C++11 has it in 1.9/15: _If a side effect on a scalar object is unsequenced relative to either anotherside effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined._ In C++03 it's a bit more complicated, see some of the answers to this: http://stackoverflow.com/questions/4176328/undefined-behavior-and-sequence-points – jogojapan Jun 06 '13 at 02:10
  • @jogojapan - right, but we're not dealing with unsequenced effects here, but with indeterminately sequenced effects. – Pete Becker Jun 06 '13 at 02:13
  • We have a two-thread discussion now :-) See my comment to the question. – jogojapan Jun 06 '13 at 02:13
  • I see. So the best way to avoid these confusions would be to have separate cout statements? – focusHard Jun 06 '13 at 02:52
  • @focusHard - yes. In general, be leery of mixing things that modify a value with things that use it. At best it will be confusing. – Pete Becker Jun 06 '13 at 02:55
0

Function evaluation in an expression depends on compiler stream operations such as << and >>. The same problem that happens when you evaluate x = f1() + f2(). You don't know which will be evaluated first because it's compiler dependent.

It would be more safe to call these on separate lines to rule out the ambiguity.

CyanCoding
  • 1,012
  • 1
  • 12
  • 35
Ryednap
  • 324
  • 1
  • 9