提问人:user1011113 提问时间:10/30/2023 更新时间:11/9/2023 访问量:193
C++14 中的放置新 + reinterpret_cast:格式良好?
Placement new + reinterpret_cast in C++14: well-formed?
问:
请考虑 C++14 中的以下示例:
alignas(T) unsigned char data[sizeof(T)];
new (data) T();
T* p = reinterpret_cast<T*>(data);
p->something(); // UB?
此代码是否合法,或者是否违反了严格别名规则,因为可能不是 ?如果它是合法的,那么标准的哪些部分明确规定了这一点?unsigned char*
T*
cppreference 有一个类似的示例,其声明必须在 C++17 中使用。这对我们没有的 C++14 意味着什么?std::launder
std::launder
// Access an object in aligned storage
const T& operator[](std::size_t pos) const
{
// Note: std::launder is needed after the change of object model in P0137R1
return *std::launder(reinterpret_cast<const T*>(&data[pos]));
}
答:
其他答复和评论已经解决了格式良好的问题。但是,您可以完全避免此问题;
您可以利用 placement- 运算符返回指向 .所以与其写:new
T
new (data) T();
T* p = reinterpret_cast<T*>(data);
你可以这样写:
T* p = new (data) T();
在这种情况下,所以完全没有必要,这是 C++14 及更早版本中的有效代码。std::launder()
评论
new
new
是的。正如您所发现的,这是您需要 C++17 的情况。对于 C++14 来说,这意味着您需要存储 的返回值,即使这会带来一些开销。std::launder
new
这并不奇怪。引入 C++17 的所有相关更改都是合理的原因之一是您在 C++14 中看到的开销。std::launder
请注意,这种开销在十年前并没有真正实现,因为它主要是错过了优化开销。只有当优化器开始变得更聪明时,它才开始发挥作用。另一方面,像这个问题这样的非法代码在旧的编译器中并不容易被破坏,即使它从来都不是合法的。
评论
launder
new
感谢大家的回复!我将尝试结合我从回答中获得的知识来回答这个问题。
不存在严格别名冲突
根据 basic.life#2:
数组对象的生存期在获得具有适当大小和对齐方式的存储时立即开始,当数组占用的存储被重用或释放时,其生存期结束
因此,在放置调用之后,数组不再包含 类型的对象。它包含一个新创建的对象。new
char
char
T
稍后,我们通过 ,这是一个有效的别名来访问该类型的对象。因此,此处不违反严格锯齿规则。T
T*
辈子
真正的问题是寿命。当我们 时,我们使用指向过期数据的指针 (),因为它已被新对象替换。不能保证指针已“更新”以指向新创建的对象。reinterpret_cast<T*>(data)
data
data
- 在 C++14 中,合法执行此操作的唯一方法是通过 placement 返回的指针访问对象。
T
new
- 此外,在 C++17 中,我们可以通过旧指针访问对象,只要我们通过 .这样就不必存储 placement 返回的指针。
data
std::launder
new
评论
std::launder()
即使 C++14 中一切正常,也没有添加。:-)此外,返回指向它所创建内容的有效指针。为什么不直接保存呢?new
T
data
std:launder
data
T