It's basically what std::vector::reserve
does but I'd appreciate an explanation about how it's done, as I didn't manage to unravel an actual implementation.
For a type T
missing a default constructor, auto buffer = ::new T[n];
is not possible.
The track, I followed so far is:
// suitably aligned memory
void* raw = ::operator new(sizeof(OverAlignedType) * n,std::align_val_t(alignof(OverAlignedType)));
// placing there an array of bytes that can provide storage AND implicitly create arrays (which are implicit lifetime types)
auto* storage = ::new (raw) std::byte[sizeof(OverAlignedType) * n];
// triggers the implicit array creation
auto* data= std::launder(reinterpret_cast<OverAlignedType*>(storage));
Same with analyzer and sanitizer
From there I could loop on storage in order to place individual OverAlignedType
objects, using an available constructor.
Is this correct or should it be fixed (and how)?
(I'm leaving aside the proper way to destroy and deallocate, for now)
I know that I have already posted several related questions on these topics but looking back to them I think that they were not quite properly answered and possibly need this reformulation.
::operator new
also implicitly creates objects. It just doesn't return a pointer to those objects like std::malloc
would. You can launder one though:
auto* data = std::launder(static_cast<OverAlignedType*>(::operator new(sizeof(OverAlignedType) * n, std::align_val_t(alignof(OverAlignedType)))));
Where you can expect the operator new
to implicitly start the lifetime of an OverAlignedType[n]
then data
points to the first element of the array.
You can then do pointer arithmetic with this like any other array pointer. For example:
for (OverAlignedType& object : std::span(data, data + n)) {
std::construct_at(&object, ...);
}
// ...
for (OverAlignedType& object : std::span(data, data + n)) {
std::destroy_at(&object);
}
::operator delete(static_cast<void*>(data), sizeof(OverAlignedType) * n, std::align_val_t(alignof(OverAlignedType)));