Is it okay to use std::array<std::byte, N> storage
as storage for allocation of some memory?
For example, is it safe to call placement-new on a.data()
, even though the lifetime of the allocated object has nothing to do with std::array
?
std::aligned_storage_t
was deprecated in C++23 for a reason, that it has UB rooted in its design (and poor API but it's not the case in this question), while it's pretty much close to this use case of std::array
, so is it mandatory to use C-style array std::byte storage[N]
as a storage for memory allocation, not std::array
(or maybe even std::vector
).
The issue with std::aligned_storage_t
is that when you start the lifetime of the new object, the lifetime of the std::aligned_storage_t
object ends. [basic.life]p1:
The lifetime of an object o of type
T
ends when:
- [...]
- the storage which he object occupies [...] is reused by an object that is not nested within o.
So if you were to use the std::array
as if it were std::aligned_storage_t
, you would not be able to call any of the member functions of the array since the arrays lifetime would end:
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
However, since you use the .data()
pointer of std::array
, then the lifetime would not end because the member std::byte[N]
would provide storage for the new object [intro.object]p3:
If a complete object is created in storage associated with another object e of type “array of N
unsigned char
” or of type “array of Nstd::byte
” that array provides storage for the created object if [...]
And by [intro.object]p4:
An object a is nested within another object b if:
- a is a subobject of b, or
- b provides storage for a, or
- the exists an object c where a is nested within c, and c is nested within b.
So instantiate the second option with b = the member std::byte[N]
of arr
and c = the newly constructed object, and the third option with c = the member std::byte[N]
and a = the newly constructed object and b = the std::array<std::byte, N>
object. The newly constructed object is nested within the std::array
.
So it can be used in the same way as if the object were std::byte[N]
instead of std::array<std::byte, N>
(namely to provide storage for a new object that is nested in the array/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`