70 Advanced The Hundred
Argument Roulette
your arguments, the compiler's order
#include <iostream>
void log_pair(int a, int b) { std::cout << a << ", " << b << "\n"; }
int main() {
int i = 0;
log_pair(i++, i++);
}
Run it. What does it print?
Answer
GCC 11 on x86-64 prints 1, 0. The very same source under Clang 18 prints 0, 1.
Both compilers are right.
Why¶
C++ deliberately leaves the evaluation order of function arguments unspecified — still
true in C++20. GCC evaluates these arguments right-to-left, so the second i++ runs first
(yielding 0) and the first then yields 1; Clang goes left-to-right and prints 0, 1.
Since C++17 the two arguments are at least indeterminately sequenced — one finishes
before the other starts — so the result is merely unpredictable; before C++17 they were
unsequenced, making this undefined behavior, not just roulette. C++17 did nail down the
neighbors: a << b << c now evaluates left-to-right, and in x.f(args) the object x is
evaluated before any argument. Both compilers smell the trap: GCC warns under -Wall
(operation on 'i' may be undefined [-Wsequence-point]), Clang even at default settings
(multiple unsequenced modifications to 'i' [-Wunsequenced]).
The fix¶
Sequence it yourself — separate statements have a guaranteed order:
Takeaway: never let two argument expressions touch the same state — if the order matters, evaluate into named locals first.
Try it: g++ -std=c++17 main.cpp -o demo && ./demo