I recently happened upon a bug in a C++ codebase which had me scratching my head. When the behaviour differed between GCC and Clang it became even more muddled.

This is the code in question

std::map<size_t, size_t> aMap; aMap[0] = aMap.size();

Simple enough, right? We’re adding an entry to the map, where the value of the entry is the size of the map. However, which size? Is it the size of the map before the insertion, or after? With C++ things are not so easy.

As it turns out, the result differs between GCC and Clang. GCC (4.9.2) will put the value of 1 into the map, i.e. the size of the map after insertion. And Clang (3.6.0) will put the value of 0 into the map, i.e. the size of the map before the insertion. Neither will print any warning during compilation. Changing optimization levels has no effect.

So how could this be?

On one hand the logical thing would be to first calculate the value on the right side of the assignment (the map size) first and then assign that to the value on the left side. This is what Clang seems to be doing.

On the other hand however the value returned by the left side is a reference to the newly inserted entry, as specified in the standard. Thus it would also make sense to first get hold of the reference before assigning a value to it, which is what GCC seems to be doing.

Or is this perhaps undefined behaviour?

Reading the standard it says in 5.17

The assignment operator (=) and the compound assignment operators all group right-to-left.

Which seems to indicate that Clang is correct. However, it also says

With respect to an indeterminately-sequenced function call, the operation of a compound assignment is a single evaluation.

This seems to indicate that something like

aMap[i] = i++;

would be undefined behaviour. But that’s not what we’re doing here, not exactly. However we are performing an operation for the left side which has a side effect on the right side.

The more succinct summary over att cppreference.com describes the order of evaluation in C++ in this way

Order of evaluation of the operands of almost all C++ operators (including the order of evaluation of function arguments in a function-call expression and the order of evaluation of the subexpressions within any expression) is unspecified. The compiler can evaluate operands in any order, and may choose another order when the same expression is evaluated again.

And this is perhaps the reason for this behaviour. If so, shouldn’t the compilers print out a warning? Perhaps some of our readers have more insight into the C++ standard?

You can check for yourself with this code

#include <map> #include <iostream> int main(int argc, char *argv[]) { std::map<size_t, size_t> aMap; aMap[0] = aMap.size(); std::cout << aMap[0] << std::endl; return 0; }

Run with

g++ -Wpedantic -O0 -o main_gcc main.cpp && ./main_gcc 1

and

clang++ -Wpedantic -O0 -o main_clang main.cpp && ./main_clang 0