使用std::array作为存储会调用UB吗?

问题描述 投票:0回答:1

使用

std::array<std::byte, N> storage
作为存储来分配一些内存可以吗? 例如,即使分配对象的生命周期与
a.data()
无关,在
std::array
上调用placement-new 是否安全?

std::aligned_storage_t
在 C++23 中被弃用,原因是它的 UB 植根于其设计(API 很差,但在这个问题中并非如此),而它与
 的用例非常接近std::array
,所以必须使用 C 风格的数组
std::byte storage[N]
作为内存分配的存储,而不是
std::array
(甚至可能是
std::vector
)。

c++ language-lawyer c++20 lifetime placement-new
1个回答
3
投票

std::aligned_storage_t
的问题是,当您开始新对象的生命周期时,
std::aligned_storage_t
对象的生命周期就结束了。 [基础生活]p1:

类型为 T 的对象

o
的生命周期结束于:

  • [...]
  • 该对象占用的存储空间[...]被未嵌套在 o 中的对象重用。

因此,如果您像使用

std::array
一样使用
std::aligned_storage_t
,您将无法调用数组的任何成员函数,因为数组的生命周期将结束:

alignas(T) std::array<std::byte, sizeof(T)> arr;
T* ptr = std::construct_at(reinterpret_cast<T*>(&arr), ...);
// Lifetime of `arr` ends
// arr.data();  // UB: lifetime of `std::array` has ended

但是,由于您使用

.data()
std::array
指针,那么生命周期不会结束,因为成员
std::byte[N]
将为新对象提供存储[intro.object]p3:

如果在与“array of N

unsigned char
”类型或“array of N
std​::​byte
”类型的另一个对象 e 关联的存储中创建了一个完整的对象,则该 array 为创建的对象提供存储 if [. ..]

[intro.object]p4:

一个对象 a 嵌套在 另一个对象 b 中,如果:

  • ab 的子对象,或者
  • ba
  • 提供存储空间
  • 存在一个对象 c,其中 a 嵌套在 c 中,c 嵌套在 b 中。

因此,用 b =

std::byte[N]
的成员
arr
c = 新构造的对象实例化第二个选项,用 c = 成员
std::byte[N]
a = 实例化第三个选项新构造的对象和 b =
std::array<std::byte, N>
对象。新构造的对象嵌套在
std::array
中。

因此它的使用方式与对象是

std::byte[N]
而不是
std::array<std::byte, N>
的方式相同(即为嵌套在数组中的新对象提供存储/
std::array
):

alignas(T) std::array<std::byte, sizeof(T)> arr;
T* ptr = std::construct_at(reinterpret_cast<T*>(arr.data()), ...);
// Lifetime of `arr` continues: The array provides storage for the `T` object.
for (std::byte repr : arr) {
    // bytes of the object representation of `*ptr`
}
T* ptr2 = std::launder(reinterpret_cast<T*>(arr.data());  // Points to the same object as `*ptr`
© www.soinside.com 2019 - 2024. All rights reserved.