为什么在 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.