Consider the following C++ code that uses Qt's container class QMap
:
#include <QMap>
#include <iostream>
QMap<int, int> myMap;
int count() {
return myMap.size();
}
int main() {
myMap[0] = count();
std::cout << myMap[0] << std::endl;
return 0;
}
Depending on whether myMap
has a new entry created in it before or after count()
is executed, this code's output will be either 1
or 0
respectively.
Does the output of this code depend on the implementation of QMap
? Or does the C++ specification make any guarantees about when count()
will be executed in relation to QMap::operator[]
? Or could the result be undefined and this is a situation that's best avoided?
I ask because I had an essentially identical situation in a program I was working on. When I compiled the program in Windows and ran it using the stock Qt 5.5.1 DLLs, the result was 0
. However, when I ran it using a different set of Qt 5.5.1 DLLs that had been compiled from source, the result was 1
. It was an awfully confusing bug that took me a little while to track down, particularly since I got different results depending on where I ran the executable!
I'm hoping that I can understand how there was two different behaviors for the same program so that I might be able to avoid bugs like this in the future.
Your problem here:
myMap[0] = count();
is that the whole assignment is an expression and the call to count()
is a sub-expression. There is no sequence point between expressions and sub-expressions.
This is not about evaluation order but about the ordering of side effects. An assignment has a side effect, in this case, it adds a new element to your QMap
. Only at a sequence point, you have the guarantee that all side effects resulting from code before the sequence point are completed.
A function call is a sequence point, but it's between the evaluation of the function arguments and the actual calls -- not related to the return value. As you don't have any arguments here, it doesn't apply in this case.
So yes, this is undefined behavior and you should avoid it. For reference, here's a quite exhaustive answer on the topic of sequence points.
The solution of course is simple: Use two separate statements. The end of a statement (;
) is always a sequence point.