为什么据称使用 `std::aligned_storage` 会导致 UB 因为它未能 "provide storage"?
Why does the use of `std::aligned_storage` allegedly cause UB due to it failing to "provide storage"?
灵感来自:
链接的提案 P1413R3
(弃用 std::aligned_storage
)说:
Using aligned_*
invokes undefined behavior (The types cannot provide storage.)
这里指的是[intro.object]/3
:
If a complete object is created ([expr.new]) in storage associated with another object e of type “array of N unsigned char
” or of type “array of N std::byte
” ([cstddef.syn]), that array provides storage for the created object if: ...
然后标准继续在一些定义中使用术语“提供存储”,但我没有看到它在任何地方说使用不同类型作为新放置的存储(未能“提供存储” ") 导致 UB。
所以,问题是:是什么让 std::aligned_storage
在用于 placement-new 时导致 UB?
C++ 标准允许一组非常有限的类型作为其他对象的存储。可以用作其他对象存储的类型集本身不能将对齐打包到它们的类型中。
想象一下:
template<std::size_t N>
using bytes=std::byte[N];
template<std::size_t S, std::size_t A>
struct alignas(A) aligned{
bytes<S> data;
};
您不能使用 &aligned<12,4>
安全地存储另一个对象。你不能用这个 属性.
来制作一个与之对齐的 typedef
您可以使用 aligned<12,4> a; &a.data
或类似的方法,但这在句法上有所不同。
现在,标准可以通过添加措辞来绕过它;但是现有的对齐存储定义没有这个神奇的措辞,并且 C++ 中的任何构造都不能拥有 aligned_storage_t
用户在没有这样的措辞的情况下所期望的属性。我的意思是,UB 就是 UB,所以编译器可以自由地解释你的程序,就好像它是一个用这种措辞的语言编写的程序......但这是用核弹打击标准错误。
论文在这方面似乎有误。
如果 std::aligned_storage_t
“提供存储”失败,那么大多数使用它会间接导致UB(见下文).
但 std::aligned_storage_t
是否真的可以“提供存储”似乎是不确定的。使用具有 alignas(Y) unsigned char arr[X];
成员(看似)的结构的常见实现 确实 根据 [intro.object]/3
“提供存储”,即使您传递了整个结构的地址进入 placement-new,而不是数组。尽管现在没有强制执行此特定实施,但我相信强制执行它会是一个简单的 non-breaking 更改。
如果std::aligned_storage_t
实际上没有“提供存储”,那么大多数用例都会导致 UB:
Placement-new 进入无法“提供存储”的对象本身是合法的,但是...
这结束了未能“提供存储”的对象的生命周期 (aligned_storage_t
),并且递归地结束了所有封闭对象。下次您访问其中任何一个时,您将获得 UB。
即使 aligned_storage_t
没有嵌套在其他对象中(这种情况很少见),销毁它时也必须小心,因为调用它的析构函数也会导致 UB,因为它的生命周期已经结束.
... The lifetime of an object o of type T ends when:
— the storage which the object occupies ... is reused by an object that is not nested within [the object]
An object a is nested within another object b if:
—a is a subobject of b, or
— b provides storage for a, or
— there exists an object c where a is nested within c, and c is nested within b.
灵感来自:
链接的提案 P1413R3
(弃用 std::aligned_storage
)说:
Using
aligned_*
invokes undefined behavior (The types cannot provide storage.)
这里指的是[intro.object]/3
:
If a complete object is created ([expr.new]) in storage associated with another object e of type “array of N
unsigned char
” or of type “array of Nstd::byte
” ([cstddef.syn]), that array provides storage for the created object if: ...
然后标准继续在一些定义中使用术语“提供存储”,但我没有看到它在任何地方说使用不同类型作为新放置的存储(未能“提供存储” ") 导致 UB。
所以,问题是:是什么让 std::aligned_storage
在用于 placement-new 时导致 UB?
C++ 标准允许一组非常有限的类型作为其他对象的存储。可以用作其他对象存储的类型集本身不能将对齐打包到它们的类型中。
想象一下:
template<std::size_t N>
using bytes=std::byte[N];
template<std::size_t S, std::size_t A>
struct alignas(A) aligned{
bytes<S> data;
};
您不能使用 &aligned<12,4>
安全地存储另一个对象。你不能用这个 属性.
您可以使用 aligned<12,4> a; &a.data
或类似的方法,但这在句法上有所不同。
现在,标准可以通过添加措辞来绕过它;但是现有的对齐存储定义没有这个神奇的措辞,并且 C++ 中的任何构造都不能拥有 aligned_storage_t
用户在没有这样的措辞的情况下所期望的属性。我的意思是,UB 就是 UB,所以编译器可以自由地解释你的程序,就好像它是一个用这种措辞的语言编写的程序......但这是用核弹打击标准错误。
论文在这方面似乎有误。
如果 std::aligned_storage_t
“提供存储”失败,那么大多数使用它会间接导致UB(见下文).
但 std::aligned_storage_t
是否真的可以“提供存储”似乎是不确定的。使用具有 alignas(Y) unsigned char arr[X];
成员(看似)的结构的常见实现 确实 根据 [intro.object]/3
“提供存储”,即使您传递了整个结构的地址进入 placement-new,而不是数组。尽管现在没有强制执行此特定实施,但我相信强制执行它会是一个简单的 non-breaking 更改。
如果std::aligned_storage_t
实际上没有“提供存储”,那么大多数用例都会导致 UB:
Placement-new 进入无法“提供存储”的对象本身是合法的,但是...
这结束了未能“提供存储”的对象的生命周期 (aligned_storage_t
),并且递归地结束了所有封闭对象。下次您访问其中任何一个时,您将获得 UB。
即使 aligned_storage_t
没有嵌套在其他对象中(这种情况很少见),销毁它时也必须小心,因为调用它的析构函数也会导致 UB,因为它的生命周期已经结束.
... The lifetime of an object o of type T ends when:
— the storage which the object occupies ... is reused by an object that is not nested within [the object]
An object a is nested within another object b if:
—a is a subobject of b, or
— b provides storage for a, or
— there exists an object c where a is nested within c, and c is nested within b.