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的当前实现似乎使用类似于上面发布的代码.这些实现的行为是否未定义?
应该如何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()似乎不是解决基于分配器的容器的一般情况的适当机制。
| 归档时间: |
|
| 查看次数: |
856 次 |
| 最近记录: |