std :: launder如何影响容器?

Muh*_*rma 11 c++ undefined-behavior c++17

考虑固定大小的向量的以下简化和不完整的实现:

template<typename T>
class Vec {
  T *start, *end;

public:
  T& operator[](ssize_t idx) { return start[idx]; }

  void pop() {
    end--;
    end->~T();
  }

  template<typename... U>
  void push(U... args) {
    new (end) T { std::forward<U>(args)... };
    end++;
  }
};
Run Code Online (Sandbox Code Playgroud)

现在考虑以下T:

struct T {
  const int i;
};
Run Code Online (Sandbox Code Playgroud)

以下用例:

Vec<T> v;
v.push(1);
std::cout << v[0].i;
v.pop();
v.push(2);
std::cout << v[0].i;
Run Code Online (Sandbox Code Playgroud)

索引运算符使用start指针来访问对象.此时的对象被销毁,pop并在其存储位置创建了另一个对象push(2).如果我正确阅读了围绕std :: launder的文档,这意味着v[0]下面一行中的行为是未定义的.


如何使用std :: launder来纠正这段代码?每次使用贴片新品时,我们是否必须开始和结束?stdlib的当前实现似乎使用类似于上面发布的代码.这些实现的行为是否未定义?

mas*_*tis 2

应该如何std::launder使用来纠正此代码?每次使用新的放置位置时,我们都必须清洗开始和结束吗?

从P0532R0launder()开始,如果将放置 new 的返回值分配给 ,则可以避免调用end。除非向量为空,否则您不需要更改起始指针,因为当前指向的对象start仍然具有您提供的代码的活动生命周期。

同一篇论文表明,launder()除非对象生命周期已结束并已被新对象替换,否则这是一个无操作,因此launder()如果不需要,则使用不会导致性能损失:

[...] 的类型std::launder(this)相当于 Richard Smith 指出的那样:请记住,launder(p)除非 p 指向其生命周期已结束并且已在同一存储中创建新对象的对象,否则这是一个无操作。

stdlib 的当前实现似乎使用与上面发布的代码类似的代码。这些实现的行为是否未定义?

是的。P0532R0也讨论了这个问题,内容与问题评论中的讨论类似:vector不直接使用placement new,placement new调用的返回值在对向量分配器的函数调用链中丢失,并且无论如何放置 new 是逐个元素使用的,因此构造内部向量机制无论如何都不能使用返回值。launder()似乎是这里要使用的工具。但是,分配器指定的指针类型根本不需要是原始指针类型,并且launder()仅适用于原始指针。对于某些类型,当前的实现目前尚未定义;launder()似乎不是解决基于分配器的容器的一般情况的适当机制。