Most low-level allocation functions return void *
.
If I want to perform pointer arithmetic on the obtained memory and/or allocate an array, is this the correct way to do it without invoking undefined behavior, given that T
is an implicit-lifetime type?
template <typename T>
auto* make_array(std::size_t N) {
return std::launder(reinterpret_cast<T*>(
::operator new(N * sizeof(T), std::align_val_t{alignof(T)})));
}
Additional background: in many code samples, I don't see the std::launder
but I believe it is necessary to access the implicitly created array. Besides, there is currently a proposal P3006 that seems to make std::launder
unecessary in this specific kind of construction, implying that it is required so far.
A similar question has already been asked but the accepted answer does not convince me, given this additional background.
Just to summarize the discussions in comment of the question (special thanks to @weijun-zhou), though the proposed snippet is not incorrect, it can be simplified to:
template <typename T>
auto* make_array(std::size_t N) {
return static_cast<T*>(::operator new(N * sizeof(T), std::align_val_t{alignof(T)}));
}
The reasoning is as follows:
::operator new
implicitly creates implicit-lifetime objects such as arrays of T
, T being an implicit-lifetime type, and produces a safely-derived pointer value (2.1) to this arrayvoid*
through a valid pointer conversion which is also a safely-derived pointer value (2.4)T*
through the same rule, ever through reinterpret_cast
or through static_cast
.The former being equivalent to static_cast<T*>(static_cast<void*>)
, using static_cast
is more straightforward and its intent clearer.
I'm missing a small piece of rule (though it's obvious) about the fact that not only the cast is legal but the values pointed to can be safely accessed.
Eventually, the same should apply to C-style allocation functions (std::malloc
,std::calloc
,std::realloc
), extra-care being required regarding alignment.