0

I have read the following: Placement new breaks consts and references? and by reading the slides from Matthis Kruse at the C++ Meeting I am confused regarding the following code

struct U { const int i; };
std::vector<U> B;
B.push_back({ 1 });
B.clear();
B.push_back({ 2 });
const int v = B[0].i; // <- Undefined Behavior

B.clear() only destructs the objects, and B.push_back({2}) uses placement new to construct a new object at the start of the memory location B has allocated.

  1. I don't understand quite why the access B[0].i is undefined behavior.
  2. Could the compiler have cached B[0].i and possibly output 1?
  3. What has std::launder to do with all this? Is it a tool to really make sure these kind of compiler optimizations cannot happen?

The standard libcxx [llvm] implementation:

template <class Tp, class Allocator>
inline typename vector<Tp, Allocator>::reference
vector<Tp, Allocator>::operator[](size_type n)
{
   assert(n < size(), "vector[] index out of bounds");
    return this->__begin_[n];
}

is the same as in the slides.

References from std 2017:

  • 8.3 (Possible violation)
  • 8.4 (mentioning std::launder)
Gabriel
  • 8,990
  • 6
  • 57
  • 101
  • 2
    This seems like a near duplicate of [How does std::launder affect containers?](https://stackoverflow.com/q/40165022/364696). – ShadowRanger Nov 28 '18 at 23:33
  • 1
    My answer to [Implementing a std::vector like container without undefined behavior](https://stackoverflow.com/a/52997191/1708801) may explain some of the details. – Shafik Yaghmour Nov 29 '18 at 03:00
  • While launder was necessary in this example for c++17, it no longer is for c++20 due to changes in basic.lifetime. So the example is no longer UB. – doug Nov 15 '22 at 05:33

1 Answers1

1

This example is no longer UB in c++20. That said, multiple push_backs will likely fail since the U copy assignment is deleted because it contains a const subobject. A copy assignment can be written using placement new which will allow normal operations with const subobjects w/o UB in c++20.

struct U { const int i; };
std::vector<U> B;
B.push_back({ 1 });
B.clear();
B.push_back({ 2 });
const int v = B[0].i; // <- No Longer Undefined Behavior in c++20

struct U { const int i; };
std::vector<U> B;
B.push_back({ 1 });
B.push_back({ 1 });  // likely to fail if the first push_back only reserves space for 1 entry (implementation dependent)
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
doug
  • 3,840
  • 1
  • 14
  • 18