std :: launder如何影响容器?

时间:2016-10-20 21:40:43

标签: 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++;
  }
};

现在考虑以下T:

struct T {
  const int i;
};

以下用例:

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

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

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

1 个答案:

答案 0 :(得分:2)

如何使用std::launder来更正此代码?我们是否必须在每次使用展示位置时开始和结束?

P0532R0开始,如果将展示位置new的返回值分配给launder(),则可以避免需要致电end。除非向量为空,否则您不需要更改起始指针,因为start当前指向的对象仍然具有您提供的代码的有效生命周期。

同一篇论文指出launder()是无操作,除非对象生命周期已经结束并且已被新对象替换,因此如果没有必要,使用launder()不会导致性能损失:

  

[...]类型std::launder(this)等同于理查德史密斯所指出的:记住launder(p)是无操作,除非p指向其生命周期已结束的对象在同一存储中创建新对象的位置。

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

是。 P0532R0也讨论了此问题,其内容与问题评论中的讨论相似:vector不直接使用展示位置新广告系列的回复价值函数链调用向量的分配器,并且在任何情况下,元素逐元素地使用,因此构造内部向量机器无论如何都不能使用返回值。 launder()似乎是此处要使用的工具。但是,分配器指定的指针类型根本不需要是原始指针类型,launder()仅适用于原始指针。目前某些类型的当前实现尚未定义; launder()似乎不是解决基于分配器的容器的通用案例的适当机制。