为什么 std::aligned_storage 在 C++23 中被弃用,用什么代替?
Why is std::aligned_storage to be deprecated in C++23 and what to use instead?
我刚刚看到 C++23 计划弃用这两个 std::aligned_storage
and std::aligned_storage_t
as well as std::aligned_union
and std::aligned_union_t
。
据我所知,在对齐存储中放置新对象并不是特别 constexpr
友好,但这似乎不是完全放弃该类型的好理由。这让我假设使用 std::aligned_storage
和我不知道的朋友还有其他一些基本问题。那会是什么?
是否有替代这些类型的提议?
以下是 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. ...
上一节来自已接受的退休提议 aligned_*
,然后是一些示例,例如这两个替换建议:
// 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)...})];
};
我刚刚看到 C++23 计划弃用这两个 std::aligned_storage
and std::aligned_storage_t
as well as std::aligned_union
and std::aligned_union_t
。
据我所知,在对齐存储中放置新对象并不是特别 constexpr
友好,但这似乎不是完全放弃该类型的好理由。这让我假设使用 std::aligned_storage
和我不知道的朋友还有其他一些基本问题。那会是什么?
是否有替代这些类型的提议?
以下是 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. ...
上一节来自已接受的退休提议 aligned_*
,然后是一些示例,例如这两个替换建议:
// 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)...})];
};