std::optional 实现为 union vs char[]/aligned_storage
std::optional implemented as union vs char[]/aligned_storage
在阅读 GCC 对 std::optional
的实现时,我注意到了一些有趣的事情。我知道boost::optional
的实现方式如下:
template <typename T>
class optional {
// ...
private:
bool has_value_;
aligned_storage<T, /* ... */> storage_;
}
但是 libstdc++ 和 libc++(以及 Abseil)都实现了它们的 optional
类型如下:
template <typename T>
class optional {
// ...
private:
struct empty_byte {};
union {
empty_byte empty_;
T value_;
};
bool has_value_;
}
在我看来,它们在功能上是相同的,但是使用一个比另一个有什么优势吗? (除了后者明显缺少 new placement 之外,这真的很不错。)
由于 post-C++17 缺陷修复,std::optional
不能 实现为对齐存储。具体来说,如果 T
是平凡可复制的,则 std::optional<T>
必须是平凡可复制的。一个union{empty; T t};
就可以满足这个要求
内部存储和放置-new
/delete
用法不能。在 C++ 内存模型中,从 TriviallyCopyable 对象到尚未包含对象的存储进行字节复制不足以实际创建该对象。相比之下,编译器生成的已参与 union
在 TriviallyCopyable 类型上的副本将是微不足道的,并将用于创建目标对象。
所以std::optional
必须这样实现。
They look to me as they are functionally identical, but are there any advantages of using one over the other? (Except for the obvious lack placement new in the latter which is really nice.)
它不仅仅是 "really nice" - 它对一个非常重要的功能至关重要,即:
constexpr std::optional<int> o(42);
在常量表达式中有几件事不能,包括new
和reinterpret_cast
。如果使用 aligned_storage
实现 optional
,则需要使用 new
创建对象并使用 reinterpret_cast
将其取回,这会阻止 optional
constexpr
友好。
使用 union
实现,您不会遇到这个问题,因此您可以在 constexpr
编程中使用 optional
(甚至在 fix for trivial copyability that is talking about, optional
已经被要求之前可用作 constexpr
).
在阅读 GCC 对 std::optional
的实现时,我注意到了一些有趣的事情。我知道boost::optional
的实现方式如下:
template <typename T>
class optional {
// ...
private:
bool has_value_;
aligned_storage<T, /* ... */> storage_;
}
但是 libstdc++ 和 libc++(以及 Abseil)都实现了它们的 optional
类型如下:
template <typename T>
class optional {
// ...
private:
struct empty_byte {};
union {
empty_byte empty_;
T value_;
};
bool has_value_;
}
在我看来,它们在功能上是相同的,但是使用一个比另一个有什么优势吗? (除了后者明显缺少 new placement 之外,这真的很不错。)
std::optional
不能 实现为对齐存储。具体来说,如果 T
是平凡可复制的,则 std::optional<T>
必须是平凡可复制的。一个union{empty; T t};
就可以满足这个要求
内部存储和放置-new
/delete
用法不能。在 C++ 内存模型中,从 TriviallyCopyable 对象到尚未包含对象的存储进行字节复制不足以实际创建该对象。相比之下,编译器生成的已参与 union
在 TriviallyCopyable 类型上的副本将是微不足道的,并将用于创建目标对象。
所以std::optional
必须这样实现。
They look to me as they are functionally identical, but are there any advantages of using one over the other? (Except for the obvious lack placement new in the latter which is really nice.)
它不仅仅是 "really nice" - 它对一个非常重要的功能至关重要,即:
constexpr std::optional<int> o(42);
在常量表达式中有几件事不能,包括new
和reinterpret_cast
。如果使用 aligned_storage
实现 optional
,则需要使用 new
创建对象并使用 reinterpret_cast
将其取回,这会阻止 optional
constexpr
友好。
使用 union
实现,您不会遇到这个问题,因此您可以在 constexpr
编程中使用 optional
(甚至在 fix for trivial copyability that optional
已经被要求之前可用作 constexpr
).