Search code examples
c++c++17c++14stdvectorassignment-operator

Weird C++14 and C++17 difference in assignment operator


I have the following code:

#include <vector>
#include <iostream>

std::vector <int> a;

int append(){
  a.emplace_back(0);
  return 10;
}

int main(){
  a = {0};
  a[0] = append();
  std::cout << a[0] << '\n';
  return 0;
}

The function append() as a side effect increases the vector size by one. Because of how vectors work, this can trigger a reallocation of its memory when exceeding its capacity.

So, when doing a[0] = append(), if a reallocation happens, then a[0] is invalidated and points to the old memory of the vector. Because of this you can expect the vector to end up being {0, 0} instead of {10, 0}, because it is assigning to the old a[0] instead of the new one.

The weird thing that confuses me is that this behavior changes between C++14 and C++17.

On C++14, the program will print 0. On C++17, it will print 10, meaning a[0] actually got 10 assigned to it. So, I have the following questions whose answers I couldn't find:

  • Is C++17 evaluating a[0]'s memory address after evaluating the RHS of the assignment expression? Does C++14 evaluate this before and that's why it changes?
  • Was this a bug that was fixed in C++17? What changed in the standard?
  • Is there a clean way to make this assignment behave like in C++17 when using C++14 or C++11?

Solution

  • This code is UB prior to C++17, as pointed out in comments, due to C++ evaluation order rules. The basic problem: Order of operations is not order of evaluation. Even something like x++ + x++ is UB.

    In C++17, sequencing rules for assignments were changed:

    1. 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