Order of evaluation of any part of any expression, including order of evaluation of function arguments is unspecified (with some exceptions listed below). The compiler can evaluate operands and other subexpressions in any order, and may choose another order when the same expression is evaluated again.
There is no concept of left-to-right or right-to-left evaluation in C++. This is not to be confused with left-to-right and right-to-left associativity of operators: the expression a() + b() + c()
is parsed as (a() + b()) + c()
due to left-to-right associativity of operator+, but c()
may be evaluated first, last, or between a()
or b()
at run time:
#include <cstdio> int a() { return std::puts("a"); } int b() { return std::puts("b"); } int c() { return std::puts("c"); } void z(int, int, int) {} int main() { z(a(), b(), c()); // all 6 permutations of output are allowed return a() + b() + c(); // all 6 permutations of output are allowed }
Possible output:
b c a c a b
Evaluation of each expression includes:
Sequenced before is an asymmetric, transitive, pair-wise relationship between evaluations within the same thread.
&&
and the built-in logical OR operator ||
is sequenced before every value computation and side effect of the second (right) argument.?:
is sequenced before every value computation and side effect associated with the second or third expression.,
is sequenced before every value computation and side effect of the second (right) argument.The rule 11 has one exception: function calls made by a standard library algorithm executing under std::execution::par_unseq execution policy are unsequenced and may be arbitrarily interleaved with each other. | (since C++17) |
operator new
) is indeterminately sequenced with respect to (until C++17)sequenced before (since C++17) the evaluation of the constructor arguments in a new-expression. 14) In a function-call expression, the expression that names the function is sequenced before every argument expression and every default argument. 15) In a function call, value computations and side effects of the initialization of every parameter are indeterminately sequenced with respect to value computations and side effects of any other parameter. 16) Every overloaded operator obeys the sequencing rules of the built-in operator it overloads when called using operator notation. 17) In a subscript expression E1[E2] , every value computation and side effect of E1 is sequenced before every value computation and side effect of E2. 18) In a pointer-to-member expression E1.*E2 or E1->*E2 , every value computation and side effect of E1 is sequenced before every value computation and side effect of E2 (unless the dynamic type of E1 does not contain the member to which E2 refers). 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. 20) In every simple assignment expression E1 = E2 and every compound assignment expression E1 @= E2 , every value computation and side effect of E2 is sequenced before every value computation and side effect of E1. 21) Every expression in a comma-separated list of expressions in a parenthesized initializer is evaluated as if for a function call (indeterminately-sequenced). | (since C++17) |
1) If a side effect on a memory location is unsequenced relative to another side effect on the same memory location, the behavior is undefined.
i = ++i + 2; // well-defined i = i++ + 2; // undefined behavior until C++17 f(i = -2, i = -2); // undefined behavior until C++17 f(++i, ++i); // undefined behavior until C++17, unspecified after C++17 i = ++i + i++; // undefined behavior
2) If a side effect on a memory location is unsequenced relative to a value computation using the value of any object in the same memory location, the behavior is undefined.
cout << i << i++; // undefined behavior until C++17 a[i] = i++; // undefined behavior until C++17 n = ++i + i; // undefined behavior
Evaluation of an expression might produce side effects, which are: accessing an object designated by a volatile lvalue, modifying an object, calling a library I/O function, or calling a function that does any of those operations.
A sequence point is a point in the execution sequence where all side effects from the previous evaluations in the sequence are complete, and no side effects of the subsequent evaluations started.
1) There is a sequence point at the end of each full-expression (typically, at the semicolon).
2) When calling a function (whether or not the function is inline and whether or not function call syntax was used), there is a sequence point after the evaluation of all function arguments (if any) which takes place before execution of any expressions or statements in the function body.
3) When returning from a function, there is a sequence point after the copy-initialization of the result of the function call, and before the destruction of all temporary objects at the end of expression in the return
statement (if any).
4) There is a sequence point after the copying of a returned value of a function and before the execution of any expressions outside the function.
5) Once the execution of a function begins, no expressions from the calling function are evaluated until execution of the called function has completed (functions cannot be interleaved).
6) In the evaluation of each of the following four expressions, using the built-in (non-overloaded) operators, there is a sequence point after the evaluation of the expression a
.
a && b a || b a ? b : c a , b
1) Between the previous and next sequence point, the value of any object in a memory location must be modified at most once by the evaluation of an expression, otherwise the behavior is undefined.
i = ++i + i++; // undefined behavior i = i++ + 1; // undefined behavior i = ++i + 1; // undefined behavior ++ ++i; // undefined behavior f(++i, ++i); // undefined behavior f(i = -1, i = -1); // undefined behavior
2) Between the previous and next sequence point, for any object in a memory location, its prior value that is modified by the evaluation of the expression must be accessed only to determine the value to be stored. If it is accessed in any other way, the behavior is undefined.
cout << i << i++; // undefined behavior a[i] = i++; // undefined behavior
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
CWG 1885 | C++11 | sequencing of the destruction of automatic variables on function return was not explicit | sequencing rules added |
CWG 1949 | C++98 | "sequenced after" was used but not defined in the C++ standard | defined as the inverse of "sequenced before" |
CWG 2146 | C++98 | the cases involving undefined behaviors did not consider bit-fields | considered |
C documentation for Order of evaluation |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/eval_order