In C ++, is there a specific order of operations when assigning an expression to an object when the expression includes that object?

Consider the following C ++ code that uses the Qt 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 there is a myMap

new entry created in it before or after count()

, this code output will be either 1

or 0

respectively.

Is the output of this code implementation dependent QMap

? Or does the C ++ spec give any guarantees as to when count()

will execute against QMap::operator[]

? Or maybe the result is undefined and this is best avoided?

I ask because I had an almost identical situation in the program I was working on. When I compiled the program on Windows and ran it using the Qt 5.5.1 libraries, the result was 0

. However, when I ran it using a different set of Qt 5.5.1 libraries that were compiled from source, the result was 1

. It was a terribly confusing error that got me going a little bit, especially since I have different results depending on where I ran the executable!

I hope I can understand how there were two different behaviors for the same program so that I can avoid such errors in the future.

+3


source to share


3 answers


Your problem is here:

myMap[0] = count();

      

is that all assignment is an expression and a call count()

is a subexpression. There is no sequence point between expressions and subpixels.

This is not an order of evaluation, but an ordering of side effects. The ledge has a side effect, in which case it adds a new element to yours QMap

. It is only at the point in the sequence that you have a guarantee that all side effects resulting from the code up to the point in the sequence are complete.



The function call is the point of the sequence, but it sits between the evaluation of the function arguments and the actual calls - not related to the return value. Since there are no arguments here, it does not apply in this case.

So this behavior is undefined and you should avoid it. A comprehensive answer to the topic of sequence points is sufficient for reference here .


The solution is, of course, simple: use two separate operators. The end of a statement ( ;

) is always a sequence point.

+6


source


Each side of the assignment can be evaluated first. It is similar to a function call (and can actually be a function call if you overloaded the assignment operator), for example:

  void f( X a, X b );

      



where one can first estimate either a or b.

This is not necessarily compiler-specific — the same compiler may choose a different evaluation order in different circumstances.

0


source


Unfortunately, as far as I know, there is no specific order, be it a specific compiler, compiler version, or library for the evaluation order, unlike a language like Python. Your code depends on undefined behavior.

There are several rules , but the compiler must comply: this is not one of them. The example they give regarding undefined behavior is similar to your example:

a[i] = i++; 

      

0


source







All Articles