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.
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 ofn
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
.