Why is std::aligned_storage to be deprecated in C++23 and what to use instead?

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 value

There is no .data() or even .data on std::aligned_* instances.
Instead, the API requires you to take the address of the object, call reinterpret_cast<T*>(...) with it, and then
finally indirect the resulting pointer giving you a T&. Not only does this mean that it cannot be used in constexpr, 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 of std::byte, potentially with a call to std::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 of aligned_*, users must call reinterpret_cast on
the address to read the bytes as T instances. Using a byte array as a replacement does not avoid this problem. That said, it’s important to recognize that continuing to use reinterpret_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)...})];
};

Leave a Comment