C ++ 17引入了一种新类型std::byte
,所以现在我们终于有了一个一流的市民类型来表示内存中的字节。除了标准上的新颖性之外,用于创建对象,生命的开始和结束,别名等的C ++规则在大多数情况下都是相当复杂的,这很直观,因此每当我觉得std::byte
是正确的工具时,紧张而又不愿使用它,因为担心会无意间召唤未定义的行为Balrogs。
一种这样的情况是用于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();
}
答案 0 :(得分:3)
t2
绝对安全并且与t1
等效吗?
不。 std::aligned_storage
创建的存储适合放置在其中的对象对齐。 std::byte buffer[sizeof(X)]
的大小正确,但对齐方式为std::byte
。通常,它的对齐方式为1
,因此与您放置的类型通常不会具有相同的对齐方式。
就C ++虚拟机而言,这不是问题,但在现实世界中,这可能会导致严重的性能下降甚至导致程序崩溃。
如果您想要适当对齐的存储,请使用参见Barry's answer std::aligned_storage
答案 1 :(得分:2)
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)>
,但不能保证它完全适合 。 specification很简单:
成员typedef
type
应该是普通的标准布局类型,适用于大小最大为Len
并且对齐方式是Align除数的任何对象的未初始化存储。
也就是说,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*
};