I vaguely remember reading somewhere that it is undefined behaviour if multiple operands in a compound expression modify the same object.
I believe an example of this UB is shown in the code below however I've compiled on g++, clang++ and visual studio and all of them print out the same values and can't seem to produce unpredictable values in different compilers.
#include <iostream>
int a( int& lhs ) { lhs -= 4; return lhs; }
int b( int& lhs ) { lhs *= 7; return lhs; }
int c( int& lhs ) { lhs += 1; return lhs; }
int d( int& lhs ) { lhs += 2; return lhs; }
int e( int& lhs ) { lhs *= 3; return lhs; }
int main( int argc, char **argv )
{
int i = 100;
int j = ( b( i ) + c( i ) ) * e( i ) / a( i ) * d( i );
std::cout << i << ", " << j << std::endl;
return 0;
}
Is this behaviour undefined or have I somehow conjured up a description of supposed UB that is not actually undefined?
I would be grateful if someone could post an example of this UB and maybe even point me to where in the C++ standard that it says it is UB.
No. It is not. Undefined behavior is out of question here (assuming the int
arithmetic does not overflow): all modifications of i
are isolated by sequence points (using C++03 terminology). There's a sequence point at the entrance to each function and there's a sequence point at the exit.
The behavior is unspecified here.
Your code actually follows the same pattern as the classic example often used to illustrate the difference between undefined and unspecified behavior. Consider this
int i = 1;
int j = ++i * ++i;
People will often claim that in this example the "result does not depend on the order of evaluation and therefore j
must always be 6". This is an invalid claim, since the behavior is undefined.
However in this example
int inc(int &i) { return ++i; }
int i = 1;
int j = inc(i) * inc(i);
the behavior is formally only unspecified. Namely, the order of evaluation is unspecified. However, since the result of the expression does not depend on the order of evaluation at all, j
is guaranteed to always end up as 6
. This is an example of how generally dangerous unspecified behavior combination can lead to perfectly defined result.
In your case the result of your expression does critically depend on the order of evaluation, which means that the result will be unpredictable. Yet, there's no undefined behavior here, i.e. the program is not allowed to format your hard drive. It is only allowed to produce unpredictable result in j
.
P.S. Again, it might turn out that some of the evaluation scenarios for your expression lead to signed integer overflow (I haven't analyzed them all), which by itself triggers undefined behavior. So, there's still a potential for unspecified behavior leading to undefined behavior in your expression. But this is probably not what your question is about.