使用`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个回答
0
投票

使用

std::array
的问题与使用
std::aligned_storage_t
相同:当您开始新对象的生命周期时,
std::array
对象的生命周期结束。 [基础生活]p1:

类型为 T 的对象

o
的生命周期结束于:

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

因此很难再次访问该对象:

alignas(T) std::array<std::byte, sizeof(T)> arr;
T* ptr = std::construct_at(reinterpret_cast<T*>(arr.data()), ...);
ptr->member();  // Fine
// reinterpret_cast<T*>(arr.data())->member();  // UB: cannot call `arr.data()` since `arr` is not a `std::array`
// std::launder(reinterpret_cast<T*>(arr.data())->member();  // UB: same as above
// reinterpret_cast<T*>(&arr)->member();  // UB: &arr does not point to *ptr
std::launder(reinterpret_cast<T*>(&arr))->member();  // OK, but confusing
// Or if you can't use alignas
template<typename T>
T* align_ptr_for_one(void* source, std::size_t size) {
    return reinterpret_cast<T*>(std::align(alignof(T), sizeof(T), ptr, sz));
}

std::array<std::byte, sizeof(T) + alignof(T)> arr;
T* ptr = std::construct_at(align_ptr_for_one<T>(arr.data(), arr.size()), ...);
ptr->member();  // Fine
// align_ptr_for_one<T>(arr.data(), arr.size())->member();  // UB: cannot call `arr.data()` since `arr` is not a `std::array`
// std::launder(align_ptr_for_one<T>(arr.data(), arr.size()))->member();  // UB: same as above
// align_ptr_for_one<T>(&arr, arr.size())->member();  // UB: align(&arr) does not point to *ptr
std::launder(align_ptr_for_one<T>(&arr, arr.size()))->member();

并非不可能,但只是容易出错,因为当数组的生命周期结束时,对原始

std::array
的任何引用都不再指向任何对象。

© www.soinside.com 2019 - 2024. All rights reserved.