c++arraysalignmentlifetime

how to create a dynamically sized array of object of possibly over-aligned not default-constructible type?


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

Full working example

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.


Solution

  • ::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)));