我最近看到一段代码使用存储缓冲区来创建对象,然后简单地交换缓冲区以避免复制开销。这是一个使用整数的简单示例:
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;
在一般情况下,这感觉就像未定义的行为(特别是交换缓冲区然后访问对象,就好像它们仍在那里一样)但我不确定标准中关于存储和放置新用法的说法。任何反馈都非常感谢!
答案 0 :(得分:1)
我怀疑有一些因素导致这种情况未定义,但我们只需要一个:
[C++11: 3.8/1]:
[..] 类型T
的对象的生命周期结束时:
- 如果
T
是具有非平凡析构函数(12.4)的类类型,则析构函数调用将启动,或者- 对象占用的存储空间被重用或已释放。
所有后续使用都是在使用寿命结束后使用,这是坏事和错误。
关键是每个缓冲区都在重用。
所以,虽然我希望这在实践中起作用,至少对于琐碎的类型(以及某些类),它是未定义的。
以下可能能够为您节省:
[C++11: 3.8/7]:
如果在对象的生命周期结束之后,在重用或释放对象占用的存储之前,在原始对象占用的存储位置创建一个新对象,指向指针对于原始对象,引用原始对象的引用,或原始对象的名称将自动引用新对象,并且一旦新对象的生命周期开始,就可以用来操作新对象 [..]
...除了你没有创建一个新对象。
在这里可能或者可能不值得注意,令人惊讶的是,随后的隐式析构函数调用都是明确定义的:
[C++11: 3.8/8]:
如果程序以静态(3.7.1),线程(3.7.2)或自动(3.7.3)存储持续时间结束T
类型对象的生命周期,并且如果T
有一个非平凡的析构函数,程序必须确保在隐式析构函数调用发生时原始类型的对象占用相同的存储位置;否则程序的行为是不确定的。