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_castis required to access the valueThere is no
.data()or even.dataonstd::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_castbeing 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_caston the address to read the bytes asTinstances. Using a byte array as a replacement does not avoid this problem. That said, it's important to recognize that continuing to usereinterpret_castwhere 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)...})];
};
