A not-so-obvious caveat when using emplace_back()
To reproduce the issue i recently came across, here is the smallest self-contained example showing the problem.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 struct  Sample  {  int  value;   int * alias;   explicit  Sample (int  val = 0 )  : value(val), alias(&value) { }; std::ostream& operator <<(std::ostream& os, const  Sample& sample) {   os << "{ value="  << sample.value      << " , alias="  << sample.alias      << " , &value="  << &sample.value << " }" ;   return  os; } int  main ()    std::vector<Sample> vec;   vec.emplace_back (1 );   std::cout << "after 1st emplace: vec[0] "  << vec[0 ] << "\n" ;      vec.emplace_back (2 );   std::cout << "after 1st emplace: vec[0] "  << vec[0 ] << "\n" ;      std::cout << "*alias = "  << *(vec[0 ].alias) << "\n" ; } 
output:
1 2 3 after 1st emplace: vec[0] { value=1 , alias=0x5a1202d0d2b0 , &value=0x5a1202d0d2b0 } after 1st emplace: vec[0] { value=1 , alias=0x5a1202d0d2b0 , &value=0x5a1202d0e2e0 } *alias = -1591726835 
The issue is:emplace_back(), memory will be reallocated to fit more data, and elements will be copied/moved to new addresses. However, float* alias is just copied/moved as a raw address, meaning its value will not be modified.
To fix this issue, we can explicitly override the default copy/move constructors that compilers generate.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 struct  Sample  {  int  value = 0 ;   int * alias = &value;   Sample () = default ;   explicit  Sample (int  v)  : value(v), alias(&value) {      Sample (const  Sample& rhs) : value (rhs.value), alias (&value) {}   Sample& operator =(const  Sample& rhs) {     value = rhs.value;      alias = &value;      return  *this ;   }      Sample (Sample&& rhs) noexcept  : value (rhs.value), alias (&value) {}   Sample& operator =(Sample&& rhs) noexcept  {     value = rhs.value;      alias = &value;      return  *this ;   } }; std::ostream& operator <<(std::ostream& os, const  Sample& sample) {   os << "{ value="  << sample.value      << " , alias="  << sample.alias      << " , &value="  << &sample.value << " }" ;   return  os; } int  main ()    std::vector<Sample> vec;   vec.emplace_back (1 );   std::cout << "after 1st emplace: vec[0] "  << vec[0 ] << "\n" ;      vec.emplace_back (2 );   std::cout << "after 1st emplace: vec[0] "  << vec[0 ] << "\n" ;      std::cout << "*alias = "  << *(vec[0 ].alias) << "\n" ; } 
output:
1 2 3 after 1st emplace: vec[0] { value=1 , alias=0x5eee05be82b0 , &value=0x5eee05be82b0 } after 1st emplace: vec[0] { value=1 , alias=0x5eee05be92e0 , &value=0x5eee05be92e0 } *alias = 1 
It’s not just emplace_back: operations such as push_back, insert, emplace, and resize can also expose the issue if they involve rearranging the container’s data.