C++17 引入了一种新类型,
std::byte
,所以现在我们终于有了一个一等公民类型来表示内存中的字节。除了在标准中是一个新颖的东西之外,C++ 的对象创建、生命周期的开始和结束、别名等规则在大多数情况下都相当复杂且不直观,所以每当我觉得 std::byte
是正确的工具时,我也会感到紧张和不愿意使用它,担心无意中召唤出未定义行为炎魔。
其中一种情况是与放置 new 一起使用的缓冲区:
#include <memory>
#include <cstddef>
#include <type_traits>
struct X { double dummy[4]; char c; };
auto t1()
{
// the old way
std::aligned_storage_t<sizeof(X)> buffer;
X* x = new (&buffer) X{};
x->~X();
}
auto t2()
{
// the new way?
std::byte buffer[sizeof(X)];
X* x = new (&buffer) X{};
x->~X();
}
t2
完全安全且与 t1
等效吗?
针对对齐问题,如何:
auto t3()
{
alignas(X) std::byte buffer[sizeof(X)];
X* x = new (&buffer) X{};
x->~X();
}
完全安全且与t2
等效吗?t1
不。事实上,两者都不好。
t2
不好,因为 NathanOliver 表示:它未对齐。你需要写:
alignas(X) std::byte storage[sizeof(X)];
t1
也有这个问题,因为你几乎肯定想写 aligned_storage_t<sizeof(X), alignof(X)>
而不仅仅是 aligned_storage_t<sizeof(X)>
。如果 X
过度对齐,您将在这里丢失它。如果 X
只是很大但没有对齐要求,那么您最终会得到相对过度对齐的存储。
t1
也因为一个特别特殊的原因而不好:aligned_storage
并不能完全保证你认为它保证的东西。特别是,它保证 X
可以适合 aligned_storage<sizeof(X)>
,但不能保证它可以完全适合。 规格很简单:
成员 typedef
应是一个简单的标准布局类型,适合用作大小至多为type
且对齐方式为 Align 的除数的任何对象的未初始化存储。Len
也就是说,
aligned_storage<16>::type
保证至少为 16 字节,但一致的实现可以轻松为您提供 32 或 4K。除了意外使用 aligned_storage<16>
而不是 aligned_storage_t<16>
的问题之外。
这就是为什么P1413作为论文存在:
aligned_storage
有点不好。
所以真正的答案实际上只是写一些类似 libstdc++ 的东西
__aligned_membuf
:
template <typename T>
struct storage_for {
alignas(T) std::byte data[sizeof(T)];
// some useful constructors and stuff, probably some getter
// that gives you a T* or a T const*
};
完全安全且与t2
等效吗?t1
不。
std::aligned_storage
创建与放置在其中的对象适当对齐的存储空间。 std::byte buffer[sizeof(X)]
虽然尺寸正确,但具有 std::byte
的对齐方式。这通常不会与您放置在其中的类型具有相同的对齐方式,因为它的对齐方式为 1
。
对于 C++ 虚拟机而言,这不是问题,但在现实世界中,这可能会导致严重的性能损失,甚至导致程序崩溃。
如果您想要适当对齐的存储,使用
请参阅Barry的答案std::aligned_storage