考虑以下C++14中的示例:
alignas(T) unsigned char data[sizeof(T)];
new (data) T();
T* p = reinterpret_cast<T*>(data);
p->something(); // UB?
字符串
由于T*
不能作为T*
的别名,因此此代码是否法律的,或者是否违反了严格别名规则?如果它是法律的,标准的哪些部分明确规定了这一点?
cppreference有一个类似的例子,它声称std::launder
必须在C17中使用。这对C14意味着什么,在那里我们没有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]));
}
型
3条答案
按热度按时间jhdbpxl91#
是的。正如你所发现的,这是一个需要C17
std::launder
的情况。这对C14来说意味着你需要存储new
的返回值,即使这会带来一些开销。这并不奇怪,引入
std::launder
的原因之一,以及所有与C17相关的更改都是合理的,这是因为你在C14中看到的开销。请注意,这个开销在十年前并没有真正实现,因为它主要是未优化的开销。只有当优化器开始变得更聪明时,它才开始计算。另一方面,像这个问题中的非法代码并不倾向于在旧编译器中破解,即使它从来都不合法。
kjthegm62#
谢谢大家的回答!我会结合我从回答中得到的知识来回答这个问题。
不存在严格别名冲突
根据basic.life#2:
数组对象的生存期从获得适当大小和对齐方式的存储空间开始,当数组占用的存储空间被重用或释放时,其生存期结束
因此,在placement
new
调用之后,char
数组不再包含char
类型的对象。它包含一个新创建的T
类型的对象。稍后,我们通过
T*
访问T
类型的对象,这是一个有效的别名。因此,这里不违反严格别名规则。终身
真实的问题是lifetime。当我们
reinterpret_cast<T*>(data)
时,我们使用的指针(data
)指向过期的数据,因为它已经被新对象替换。不能保证data
指针被“更新”为指向新创建的对象。new
返回的指针访问对象T
。data
访问对象,只要我们通过std::launder
清洗它。这允许不必存储由放置new
返回的指针。m2xkgtsf3#
其他的答案和评论已经解决了格式良好的问题。然而,你可以完全避免这个问题;你可以利用placement-
new
操作符返回一个指向T
的有效指针的事实。所以不用写:字符串
你可以这样写:
型
所以
std::launder()
在这种情况下根本不是必需的,这是C++14和更早版本中的有效代码。