I must be blind or something

Just doing some sample test. I came across this.
Weird thing is, I remember doing this same snippet before, and I had no problem.
Did CERN mess up reality or what the mama?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
using namespace std;
class A {
public:
	A() { a.a = a.b = 1; }
	struct { int a,b; } a;
	int b(void);
};
int A::b(void) { int x = a.a; a.a = a.b; a.b = x; return x; };
int main(void) {
	A a;
	a.a.a = 0;
	a.b();
// uncomment ONLY one of next 2 options:
// ======== OPTION 1 ========= (this outputs "11")
//	cout << a.b();
//	cout << a.a.b << endl;
// ======== OPTION 2 ========= (this outputs "10" - WHY?)
//	cout << a.b() << a.a.b << endl;
// ===========================
	return 0;
}
The order of evaluation of subexpressions (beyond what is required by precedence rules and sequence point rules) is not defined. The expression cout << a.b() << a.a.b << endl may be evaluated as
1
2
3
4
5
6
7
8
//--------
//These evaluations may happen in any order.
auto subexpression_value4 = std::endl;
auto subexpression_value3 = a.a.b;
auto subexpression_value2 = a.b();
auto subexpression_value1 = std::cout;
//--------
subexpression_value1 << subexpression_value2 << subexpression_value3  << subexpression_value4;
Last edited on
Well then, how is it fair to put this as a question in an exam, asking you to determine the output of the snippet?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
using namespace std;
class A {
    public:
        A() { a.a = a.b = 1; }
        struct { int a,b; } a;
        int b(void);
};
int A::b(void) { int x=a.a;a.a=a.b;a.b=x; return x; };
int main(void) {
        A a;
        a.a.a = 0;
        a.b();
        cout << a.b() << a.a.b << endl;
        return 0;
}


When I parsed this, I came to the conclusion that "11" is the correct output...

Nevertheless, it is "10". Whoever wrote this snippet, did not bother with the order of evaluations, just compiled it and noted the output and decided the correct answer.

Would you come to a different conclusion than me?
Well, why are you telling us? You should go tell whoever gave you that exam, so they can fix it.

But, nothing shocking about it. Programming teachers are usually pretty ignorant about the more obscure rules of a given language; C++ teachers doubly so. As the saying goes, those who can't do, teach.
The snippet above is not from an actual exam, it's just an example of a possible exam, a sample test.

I actually found the first snippet I was remembering about:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
What is the output of the following program?
 
#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;
   a.b();
   cout << a.b();
   cout << a.a[1] << endl;
   return 0;
}

1 - the program will cause a compilation error
2 - 00
3 - 11
4 - 10


This one uses an array instead of the struct, but the algorithm is the same.

But the big difference is that here, the 2 console outputs are put in 2 different cout statements...
And the correct answer is now actually predictable.
> the correct answer is now actually predictable.


The output of the first snippet is also predictable (both options result in the output "11") in the current incarnation of the language (C++17).
The implementation has no choice in the matter; the order of evaluation is clearly specified.
16) Every overloaded operator obeys the sequencing rules of the built-in operator it overloads when called using operator notation.
19) In a shift operator expression E1<<E2 and E1>>E2, every value computation and side-effect of E1 is sequenced before every value computation and side effect of E2
http://en.cppreference.com/w/cpp/language/eval_order

The rationale for this change in the language is explained in the proposal: 'Refining Expression Evaluation Order for Idiomatic C++' : http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0145r3.pdf
Last edited on
So << is a sequence point now? Does that mean that x << a++ << a++ is no longer undefined?
They ought to just get it over with and define order of evaluation.
> So << is a sequence point now?

The concept of sequence points was subsumed by the sequenced-before rules in C++11.
The obsoleted C++98 sequence point rules were inaccurately/loosely specified even in C++98 (which was not concurrency-aware).


> Does that mean that x << a++ << a++ is no longer undefined?

Yes, no longer undefined after C++17.

More examples from cppreference (assumes that i is a scalar):
1
2
3
4
5
6
7
8
9
10
11
i = ++i + 2;       // undefined behavior until C++11, well-defined in C++11

i = i++ + 2;       // undefined behavior until C++17, well-defined in C++17
f(i = -2, i = -2); // undefined behavior until C++17, well-defined in C++17
cout << i << i++; // undefined behavior until C++17, well-defined in C++17
a[i] = i++;       // undefined behavior until C++17, well-defined in C++17

f(++i, ++i);       // undefined behavior until C++17, unspecified behaviour in C++17

i = ++i + i++;     // undefined behavior even in C++17
n = ++i + i;    // undefined behavior even in C++17 

http://en.cppreference.com/w/cpp/language/eval_order

For more information, read: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0145r3.pdf
Registered users can post here. Sign in or register to post.