标准::aligned_storage 中的新放置

Placement new in std::aligned_storage?

本文关键字:新放 storage aligned 标准      更新时间:2023-10-16

假设我有一个类型模板参数 T。

假设我有一个如下std::aligned_storage

typename std::aligned_storage<sizeof(T), alignof(T)>::type storage;

我想在storage中放置新的 T .

要传递给放置新运算符的符合标准的指针值/类型是什么,如何从storage派生?

new (& ???) T(a,b,c);

例如:

new (&storage) T(a,b,c);
new (static_cast<void*>(&storage)) T(a,b,c);
new (reinterpret_cast<T*>(&storage)) T(a,b,c);
new (static_cast<T*>(static_cast<void*>(&storage));

以上哪项(如果有)是合规的,如果没有,更好的方法是什么?

最偏执的方式是

::new ((void *)::std::addressof(storage)) T(a, b, c);

解释:

  • ::std::addressof防止storage上过载的一元operator&,这是标准在技术上允许的。(尽管没有理智的实现可以做到这一点。该::std可防止任何可能位于范围内的称为std的非顶级命名空间(或类)。
  • (void *)(在这种情况下相当于static_cast)确保您将放置称为operator new采用void *而不是其他类似decltype(storage) *
  • ::new 跳过任何特定于类的放置operator new s,确保调用转到全局放置。

总之,这保证了调用在void * operator new转到库放置,并且T是在storage所在的位置构造的。

然而,在大多数理智的程序中,

new (&storage) T(a,b,c);

应该足够了。

放置分配函数描述如下 (C++14 n4140 18.6.1.3):

void* operator new(std::size_t size, void* ptr) noexcept;

返回: ptr .

备注:故意不执行其他操作。

20.10.7.6 表57这样描述aligned_storage<Len, Align>

成员类型定义type 应为适合使用的 POD 类型 作为任何对象的未初始化存储 谁的大小最多是 Len 和谁 对齐是对齐的除数

这意味着在您的情况下,&storage适合放置类型 T 的对象。因此,在正常情况下1,您列出的所有 4 种调用放置new的方式都是有效且等效的。为了简洁起见,我会使用第一个(new (&storage))。


1 T.C. 在评论中正确地指出,从技术上讲,您的程序可以声明分配typename std::aligned_storage<sizeof(T), alignof(T)>::type*函数的重载,然后通过重载分辨率而不是库提供的"放置新"版本来选择。

我会说这在至少 99.999% 的情况下不太可能,但如果您还需要防范这种情况,请使用其中一个强制void*。直接static_cast<void*>(&storage)就足够了。

此外,如果你偏执到这个级别,你可能应该使用::new而不是仅仅new来绕过任何特定于类的分配函数。