Sunday 20 March 2016

c++ - Unexpected order of evaluation (compiler bug?)











I'm not sure if this is a gcc bug or not, so I'll ask:



unsigned int n = 0;
std::cout << n++ << n << ++n;



gcc gives the extremely strange result:
"122" which AFAICT is impossible. Because << is left associative, it should be the same as:



operator<<(operator<<(operator<<(std::cout, n++), n), ++n)


and because there is a sequence point before and after evaluating arguments, n is never modified twice (or even accessed) between two sequence points -- so it shouldn't be undefined behaviour, just the order of evaluation unspecified.




So AFAICT valid results would be:
111
012
002
101



and nothing else


Answer



There is a sequence point between evaluating arguments and calling a function. There is no sequence point between evaluating different arguments.




Let's look at the outermost function call:



operator<<(operator<<(operator<<(std::cout, n++), n), ++n)


The arguments are




  • operator<<(operator<<(std::cout, n++), n)




and





It is unspecified which of these is evaluated first. It's also allowed that the first argument is partially evaluated when the second argument is evaluated.



From the standard, section [intro.execution] (wording from draft 3225):






  • If A is not sequenced before
    B and B is not sequenced before A, then A and B are unsequenced. [ Note: The execution of unsequenced
    evaluations can overlap. — end note ]


  • Except where noted, evaluations of operands of individual operators and of subexpressions of individual
    expressions are unsequenced. [ Note: In an expression that is evaluated more than once during the execution
    of a program, unsequenced and indeterminately sequenced evaluations of its subexpressions need not be
    performed consistently in different evaluations. — end note ] The value computations of the operands of an
    operator are sequenced before the value computation of the result of the operator. If a side effect on a scalar
    object is unsequenced relative to either another side effect on the same scalar object or a value computation

    using the value of the same scalar object, the behavior is undefined.





Because you have multiple operations with side effects on the same scalar object which are unsequenced with respect to each other, you're in that realm of undefined behavior, and even 999 would be a permissible output.


No comments:

Post a Comment

c++ - Does curly brackets matter for empty constructor?

Those brackets declare an empty, inline constructor. In that case, with them, the constructor does exist, it merely does nothing more than t...