为什么在 std::optional 的某些实现中有一个虚拟联合成员?
Why is there a dummy union member in some implementations of std::optional?
libstdc++ (GNU) 和 libc++ (LLVM) 都使用联合实现 std::optional
值存储,并且它们都包含一个虚拟成员。
GNU 实现:
using _Stored_type = remove_const_t<_Tp>;
struct _Empty_byte { };
union {
_Empty_byte _M_empty;
_Stored_type _M_payload;
};
LLVM 实现:
union
{
char __null_state_;
value_type __val_;
};
我的问题是:为什么我们需要这些 _M_empty
/__null_state_
成员?单人工会有问题吗?
单个成员联合会在尝试默认可构造和 constexpr 兼容时产生各种问题。
考虑这段代码:
struct nontrivial {
constexpr nontrivial(int o) : u{o} {}
int u;
};
union storage {
nontrivial nt;
};
struct optional {
storage s;
};
constexpr auto run() -> int {
optional o;
return o.s.nt.u;
}
int main() {
constexpr int t = run();
}
这是错误的,因为 optional
有一个已删除的构造函数。
那么一个简单的解决方法是添加一个不初始化联合成员的构造函数:
union storage {
constexpr storage() {} // standard says no
nontrivial nt;
};
但是不行。 Constexpr 联合体必须至少有一名活跃成员。它不能是空联合。为了解决此限制,添加了一个虚拟成员。这使得 std::optional
在 constexpr 上下文中可用。
(感谢@Barry!)来自 [dcl.constexpr]/4(强调我的):
The definition of a constexpr constructor whose function-body is not = delete shall additionally satisfy the following requirements:
if the class is a union having variant members ([class.union]), exactly one of them shall be initialized;
if the class is a union-like class, but is not a union, for each of its anonymous union members having variant members, exactly one of them shall be initialized;
for a non-delegating constructor, every constructor selected to initialize non-static data members and base class subobjects shall be a constexpr constructor;
for a delegating constructor, the target constructor shall be a constexpr constructor.
libstdc++ (GNU) 和 libc++ (LLVM) 都使用联合实现 std::optional
值存储,并且它们都包含一个虚拟成员。
GNU 实现:
using _Stored_type = remove_const_t<_Tp>;
struct _Empty_byte { };
union {
_Empty_byte _M_empty;
_Stored_type _M_payload;
};
LLVM 实现:
union
{
char __null_state_;
value_type __val_;
};
我的问题是:为什么我们需要这些 _M_empty
/__null_state_
成员?单人工会有问题吗?
单个成员联合会在尝试默认可构造和 constexpr 兼容时产生各种问题。
考虑这段代码:
struct nontrivial {
constexpr nontrivial(int o) : u{o} {}
int u;
};
union storage {
nontrivial nt;
};
struct optional {
storage s;
};
constexpr auto run() -> int {
optional o;
return o.s.nt.u;
}
int main() {
constexpr int t = run();
}
这是错误的,因为 optional
有一个已删除的构造函数。
那么一个简单的解决方法是添加一个不初始化联合成员的构造函数:
union storage {
constexpr storage() {} // standard says no
nontrivial nt;
};
但是不行。 Constexpr 联合体必须至少有一名活跃成员。它不能是空联合。为了解决此限制,添加了一个虚拟成员。这使得 std::optional
在 constexpr 上下文中可用。
(感谢@Barry!)来自 [dcl.constexpr]/4(强调我的):
The definition of a constexpr constructor whose function-body is not = delete shall additionally satisfy the following requirements:
if the class is a union having variant members ([class.union]), exactly one of them shall be initialized;
if the class is a union-like class, but is not a union, for each of its anonymous union members having variant members, exactly one of them shall be initialized;
for a non-delegating constructor, every constructor selected to initialize non-static data members and base class subobjects shall be a constexpr constructor;
for a delegating constructor, the target constructor shall be a constexpr constructor.