int a = 0;
cout<<(a++, ++a, a++, ++a, a++)<<"\n";
I am doubting that the above code isn't undefined behavior, and I want to verify that.
I know the following code has UB:
int a = 0;
cout << a++ << " " << ++a << " " << a++ << "\n";
int a = 0;
cout<<(a++, ++a, a++, ++a, a++)<<"\n";
I am doubting that the above code isn't undefined behavior, and I want to verify that.
I know the following code has UB:
int a = 0;
cout << a++ << " " << ++a << " " << a++ << "\n";
The first example does not have undefined behaviour because it uses the built-in comma operator. The built-in comma operator guarantees that the left operand is sequenced before the right operand. That means that all value computations and side effects of the left operand occur before all value computations and side effects of the right operand. For example, both the read from a
and the write to a
that are performed by the first a++
occur before both the read from a
and the write to a
that are performed by the first ++a
, and so on.
The second example has well-defined behavior in C++17 and later because in C++17 and later it is guaranteed that in a shift expression E1 << E2
, the expression E1
is sequenced before E2
. Note that in this case the <<
operator means a call to an overloaded operator<<
function; however, a use of an operator that invokes an overloaded operator function still has the same sequencing rules as the built-in operator (see here; credit goes to HolyBlackCat for pointing this out).
But before C++17, the second example has undefined behavior because the rule that E1
is sequenced before E2
did not exist. To understand the behavior of the expression, we would need to write it out in terms of the actual calls to the overloaded operator functions:
operator<<((operator<<(cout.operator<<(a++), " ")).operator<<(a++), " ").operator<<(a++);
Here, there are no comma operators involved. The commas here only separate function arguments from each other but do not invoke the built-in comma operator.
Because C++ prior to C++17 offers no guarantees that the expression denoting a function to be called is evaluated before its arguments, the three a++
s are unsequenced. These unsequenced modifications have undefined behavior.