Unraveling the Mystery: Understanding Function Argument Evaluation Order in C++

Let's explore "Don’t depend on the order of evaluation of function arguments". You might be wondering what I'm talking about because many developers assume that function arguments are evaluated from left to right. That's incorrect! There are no guarantees!
Reason: The order is unspecified.
Note: C++17 has stricter rules for the order of evaluation, but the order of function argument evaluation is still unspecified.
Let's look at a simple example:
//test.cpp
#include <iostream>
void foo(int a, int b) {
std::cout << "a: " << a << " " << "b: " << b << std::endl;
}
int main() {
int i = 0;
foo(i++, i++); // (1, 0) | (0, 1) | (0, 0)
return 0;
}
If you understand what I'm saying, then you're on the right track. If not, let's move on to the explanation.
Here's my proof. The output from gcc and clang is different:
- gcc
g++ -std=c++2a -O3 -Wall test.cpp -o test && ./test
test.cpp: In function ‘int main()’:
test.cpp:9:15: warning: operation on ‘i’ may be undefined [-Wsequence-point]
9 | foo(i++, i++); // (1, 0) | (0, 1) | (0, 0)
| ~^~
test.cpp:9:15: warning: operation on ‘i’ may be undefined [-Wsequence-point]
a: 1 b: 0
- clang
clang++ -std=c++2a -O3 -Wall test.cpp -o test && ./test
test.cpp:9:10: warning: multiple unsequenced modifications to 'i' [-Wunsequenced]
foo(i++, i++); // (1, 0) | (0, 1) | (0, 0)
^ ~~
1 warning generated.
a: 0 b: 1
The function call could be evaluated as foo(0, 1) or foo(1, 0), but the order is not guaranteed. Technically, the behavior is undefined. In C++17, this code doesn't have undefined behavior, but it still doesn't specify which argument is evaluated first.
I compile in C++20 or use the best error detection during compilation with optimization by using a compiler flag.
If you want to learn more about this, check out these links:


