在C++14中放置new + reinterpret_cast:格式良好?

thigvfpy  于 11个月前  发布在  其他
关注(0)|答案(3)|浏览(73)

考虑以下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]));
    }

jhdbpxl9

jhdbpxl91#

是的。正如你所发现的,这是一个需要C17 std::launder的情况。这对C14来说意味着你需要存储new的返回值,即使这会带来一些开销。
这并不奇怪,引入std::launder的原因之一,以及所有与C17相关的更改都是合理的,这是因为你在C14中看到的开销。
请注意,这个开销在十年前并没有真正实现,因为它主要是未优化的开销。只有当优化器开始变得更聪明时,它才开始计算。另一方面,像这个问题中的非法代码并不倾向于在旧编译器中破解,即使它从来都不合法。

kjthegm6

kjthegm62#

谢谢大家的回答!我会结合我从回答中得到的知识来回答这个问题。

不存在严格别名冲突

根据basic.life#2:
数组对象的生存期从获得适当大小和对齐方式的存储空间开始,当数组占用的存储空间被重用或释放时,其生存期结束
因此,在placement new调用之后,char数组不再包含char类型的对象。它包含一个新创建的T类型的对象。
稍后,我们通过T*访问T类型的对象,这是一个有效的别名。因此,这里不违反严格别名规则。

终身
真实的问题是lifetime。当我们reinterpret_cast<T*>(data)时,我们使用的指针(data)指向过期的数据,因为它已经被新对象替换。不能保证data指针被“更新”为指向新创建的对象。

  • 在C++14中,唯一合法的方法是通过placement new返回的指针访问对象T
  • 在C++17中,另外,我们可以通过旧指针data访问对象,只要我们通过std::launder清洗它。这允许不必存储由放置new返回的指针。
m2xkgtsf

m2xkgtsf3#

其他的答案和评论已经解决了格式良好的问题。然而,你可以完全避免这个问题;你可以利用placement-new操作符返回一个指向T的有效指针的事实。所以不用写:

new (data) T();
T* p = reinterpret_cast<T*>(data);

字符串
你可以这样写:

T* p = new (data) T();


所以std::launder()在这种情况下根本不是必需的,这是C++14和更早版本中的有效代码。

相关问题