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

Time:04-12

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 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)...})];
};
  • Related