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)...})];
};
aligned_*
invokes undefined behavior". I've asked a question about this: Why does the use ofstd::aligned_storage
allegedly cause UB due to it failing to "provide storage"? – Pernod