Inspired by: Why is std::aligned_storage to be deprecated in C++23 and what to use instead?
The linked proposal P1413R3
(that deprecates std::aligned_storage
) says that:
Using
aligned_*
invokes undefined behavior (The types cannot provide storage.)
This refers to [intro.object]/3
:
If a complete object is created ([expr.new]) in storage associated with another object e of type “array of N
unsigned char
” or of type “array of Nstd::byte
” ([cstddef.syn]), that array provides storage for the created object if: ...
The standard then goes on to use the term "provides storage" in a few definitions, but I don't see it saying anywhere that using a different type as storage for placement-new (that fails to "provide storage") causes UB.
So, the question is: What makes std::aligned_storage
cause UB when used for placement-new?
The paper appears to be wrong on this.
If std::aligned_storage_t
failed to "provide storage", then most uses of it would indirectly cause UB (see below).
But whether std::aligned_storage_t
can actually "provide storage" appears to be unspecified. A common implementation that uses a struct with alignas(Y) unsigned char arr[X];
member (seemingly) does "provide storage" according to [intro.object]/3
, even if you pass the address of the whole structure into placement-new, rather than the array. Even though this specific implementation isn't mandated now, I believe mandating it would be a simple non-breaking change.
If std::aligned_storage_t
actually didn't "provide storage", then most use cases would cause UB:
Placement-new into an object that fails to "provide storage" is legal by itself, but...
This ends the lifetime of the object that failed to "provide storage" (aligned_storage_t
), and, recursively, all enclosing objects. The next time you access any of those, you get UB.
Even if aligned_storage_t
is not nested within other objects (which is rare), you'd have to be careful when destroying it, since calling its destructor would also cause UB, since its lifetime has already ended.
... The lifetime of an object o of type T ends when:
— the storage which the object occupies ... is reused by an object that is not nested within [the object]
An object a is nested within another object b if:
—a is a subobject of b, or
— b provides storage for a, or
— there exists an object c where a is nested within c, and c is nested within b.