Skip to main content
Stack Overflow
  1. About
  2. For Teams

Return to Answer

added 632 characters in body
Source Link
Artyer
  • 44.3k
  • 3
  • 82
  • 109

The problem with using std::array is the same asissue with std::aligned_storage_t: When is that when you start the lifetime of the new object, the lifetime of the std::arrayaligned_storage_t object ends. [basic.life]p1:

So if you were to use the std::array as if it is just difficultwere std::aligned_storage_t, you would not be able to accesscall any of the object againmember 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.data()&arr), ...);
ptr->member(); // Fine
// reinterpret_cast<T*>(arr.data())->member(); // UB: cannot call `arr.data()`Lifetime sinceof `arr` is not a `std::array`ends
// std::launder(reinterpret_cast<T*>(arr.data())->member(); // UB: same as above
// reinterpret_cast<T*>(&arr)->member(); // UB: &arr does not pointlifetime toof *ptr
std`std::launder(reinterpret_cast<T*>(&arr))->member(); // OK,array` buthas confusingended

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 Nunsigned 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):

// 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>reinterpret_cast<T*>(arr.data(), arr.size()), ...);
ptr->member(); // Fine
// align_ptr_for_one<T>(arr.data(), arr.size())->member();Lifetime of //`arr` UBcontinues: cannot callThe `arr.data()`array sinceprovides `arr`storage isfor notthe a`T` `std::array`object.
//for (std::launder(align_ptr_for_one<T>(arr.data(), arr.size()))->member(); byte //repr UB: same asarr) above{
// align_ptr_for_one<T>(&arr, arr.size())->member(); // UB:bytes align(&arr)of doesthe notobject pointrepresentation toof *ptr`*ptr`
}
T* ptr2 = std::launder(align_ptr_for_one<T>reinterpret_cast<T*>(&arr, arr.sizedata()))->member(); // Points to the same object as `*ptr`

Not impossible, but just error prone since any references to the original std::array no longer point to any object when the array's lifetime ends.

The problem with using std::array is the same as with std::aligned_storage_t: When you start the lifetime of the new object, the lifetime of the std::array object ends. [basic.life]p1:

So it is just difficult to access the object again:

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();

Not impossible, but just error prone since any references to the original std::array no longer point to any object when the array's lifetime ends.

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:

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 Nunsigned 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`
Source Link
Artyer
  • 44.3k
  • 3
  • 82
  • 109

The problem with using std::array is the same as with std::aligned_storage_t: When you start the lifetime of the new object, the lifetime of the std::array 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 it is just difficult to access the object again:

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();

Not impossible, but just error prone since any references to the original std::array no longer point to any object when the array's lifetime ends.

lang-cpp

AltStyle によって変換されたページ (->オリジナル) /