Unexpected result when C++ store element into std::vector from return value of function

When the function involves reallocation, I found some compilers may save the address before the function call. It leads the return value stored in the invalid address.

There is an example to explain behavior in above description.

#include <stdio.h>
#include <vector> 
using namespace std;

vector<int> A; 
int func() { 
    A.push_back(3);
    A.push_back(4);
    return 5; 
} 
int main() { 
    A.reserve(2);
    A.push_back(0);
    A.push_back(1);
    A[1] = func();
    printf("%d\n", A[1]);
    return 0;
}

There are some common C++ compiler, and the test result as follows.

  • GCC(GNU Compiler Collection): Runtime Error or output 1
  • Clang: output 5
  • VC++: output 5

Is it undefined behavior?


Solution 1:

The behaviour is undefined in all C++ versions before C++17. The simple reason is that the two sides of the assignment operator can be evaluated in any order:

  • Assuming A[1] is evaluated first, you get an int& referring to the second element of A at that point.
  • Then, the func() is evaluated, which can reallocate the storage for the vector, leaving the previously retrieved int& a dangling reference.
  • Finally, the assignment is performed, writing to unallocated storage. Since the standard allocators cache memory, the OS often won't catch this error.

Only in C++17, the special rule 20 for the assignment was made:

In every simple assignment expression E1=E2 and every compound assignment expression E1@=E2, every value computation and side-effect of E2 is sequenced before every value computation and side effect of E1

With C++17, A[1] must be evaluated after the call to func(), which then provides defined, reliable behaviour.