Search code examples
c++memory-managementc++17allocatorstdlaunder

Is std::launder needed after std::uninitialized_default_construct


I have a code similar to the following which uses an allocator to allocate raw memory, and then uses std::uninitialized_default_construct_n (or another function of the same family) to construct objects in it.

std::allocator<T> allocator;
T* buffer = allocator.allocate(n);
std::uninitialized_default_construct_n(buffer, n);
buffer = std::launder(buffer); // needed?
...
std::destroy_n(buffer, n);
allocator.deallocate(buffer, n);

Is it needed to use std::launder on the pointer after this, to inform the compiler that the pointer now points to T objects? Or do the construct functions do the same thing implicitly?

It is for a container class, where I want to avoid value-constructing (instead of default-constructing) objects that will later be copy-assigned into.


Solution

  • No, std::launder may only be necessary in the allocator itself, not at the point where it's used. Note the effect of allocate:

    a.allocate(n)

    Result: XX​::​pointer
    Effects: Memory is allocated for an array of n T and such an object is created but array elements are not constructed.

    [Example 1: When reusing storage denoted by some pointer value p, launder(reinterpret_cast<T*>(new (p) byte[n * sizeof(T)])) can be used to implicitly create a suitable array object and obtain a pointer to it. — end example]

    - [allocator.requirements.general] allocate

    Furthermore, std::allocator::allocate

    Returns: A pointer to the initial element of an array of n T.

    Even though the lifetime of the array elements has not yet begun, the lifetime of the surrounding array has begun. std::uninitialized_default_construct_n begins the lifetime of elements and does not end the lifetime of the surrounding array. This means that the elements of the array can be reached normally using the returned buffer pointer, which can be used in limited ways ([basic.life] p7).

    std::launder would only be necessary if you were given a pointer whose provenance does not lead back to a T object (or array thereof) at all. Example 1 in the quote shows how someone who implements allocate could use launder.