I just saw that C 23 plans to deprecate both std::aligned_storage
and std::aligned_storage_t
as well as std::aligned_union
and std::aligned_union_t
.
Placement new'd objects in aligned storage are not particularly constexpr
friendly as far as I understand, but that doesn't appear to be a good reason to throw out the type completely. This leads me to assume that there is some other fundamental problem with using std::aligned_storage
and friends that I am not aware of. What would that be?
And is there a proposed alternative to these types?
CodePudding user response:
Here are three excerpts from P1413R3
:
Background
aligned_*
are harmful to codebases and should not be used. At a high level:
- Using
aligned_*
invokes undefined behavior (The types cannot provide storage.)- The guarantees are incorrect (The standard only requires that the type be at least as large as requested but does not put an upper bound on the size.)
- The API is wrong for a plethora of reasons (See "On the API".)
- Because the API is wrong, almost all usage involves the same repeated pre-work (See "Existing usage".)
On the API
std::aligned_*
suffer from many poor API design decisions. Some of these are shared, and some are specific to each. As for what is shared, there are three main problems [only one is included here for brevity]:
- Using
reinterpret_cast
is required to access the valueThere is no
.data()
or even.data
onstd::aligned_*
instances. Instead, the API requires you to take the address of the object, callreinterpret_cast<T*>(...)
with it, and then finally indirect the resulting pointer giving you aT&
. Not only does this mean that it cannot be used inconstexpr
, but at runtime it's much easier to accidentally invoke undefined behavior.reinterpret_cast
being a requirement for use of an API is unacceptable.
Suggested replacement
The easiest replacement for
aligned_*
is actually not a library feature. Instead, users should use a properly-aligned array ofstd::byte
, potentially with a call tostd::max(std::initializer_list<T>)
. These can be found in the<cstddef>
and<algorithm>
headers, respectively (with examples at the end of this section). Unfortunately, this replacement is not ideal. To access the value ofaligned_*
, users must callreinterpret_cast
on the address to read the bytes asT
instances. Using a byte array as a replacement does not avoid this problem. That said, it's important to recognize that continuing to usereinterpret_cast
where it already exists is not nearly as bad as newly introducing it where it was previously not present. ...
The above section from the accepted proposal to retire aligned_*
is then followed with a number of examples, like these two replacement suggestions:
// To replace std::aligned_storage
template <typename T>
class MyContainer {
private:
//std::aligned_storage_t<sizeof(T), alignof(T)> t_buff;
alignas(T) std::byte t_buff[sizeof(T)];
};
// To replace std::aligned_union
template <typename... Ts>
class MyContainer {
private:
//std::aligned_union_t<0, Ts...> t_buff;
alignas(Ts...) std::byte t_buff[std::max({sizeof(Ts)...})];
};