交换包含放置新创建对象的存储缓冲区

Swapping storage buffers containing placement new created objects

我最近看到一段代码,它使用存储缓冲区来创建对象,然后简单地交换缓冲区以避免复制开销。这是一个使用整数的简单示例:

std::aligned_storage_t<sizeof(int), alignof(int)> storage1;
std::aligned_storage_t<sizeof(int), alignof(int)> storage2;

new (&storage1) int(1);
new (&storage2) int(2);

std::swap(storage1, storage2);

int i1 = reinterpret_cast<int&>(storage1);
int i2 = reinterpret_cast<int&>(storage2);

//this prints 2 1
std::cout << i1 << " " << i2 << std::endl;

这在一般情况下感觉像是未定义的行为(特别是交换缓冲区然后访问对象,就好像它们仍然存在一样)但我不确定标准对这种存储和新放置的使用有何看法。非常感谢任何反馈!

我怀疑有几个因素导致这个未定义,但我们只需要一个:

[C++11: 3.8/1]: [..] The lifetime of an object of type T ends when:

  • if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or
  • the storage which the object occupies is reused or released.

所有后续使用都是报废后使用,这是错误的。

关键是每个缓冲区都在重复使用。

所以,虽然我希望这至少对琐碎的类型(以及某些类)在实践中有效,但它是未定义的。


以下可能可以拯救您:

[C++11: 3.8/7]: If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object [..]

...除非您没有创建新对象。

这里可能值得注意也可能不值得注意,令人惊讶的是,随后的隐式析构函数调用都是明确定义的:

[C++11: 3.8/8]: If a program ends the lifetime of an object of type T with static (3.7.1), thread (3.7.2), or automatic (3.7.3) storage duration and if T has a non-trivial destructor, the program must ensure that an object of the original type occupies that same storage location when the implicit destructor call takes place; otherwise the behavior of the program is undefined.