在 P2641r4:检查联合替代方案是否处于活动状态中,作者提供了
optional<bool>
的实现作为激励示例,并声称这是格式良好的。
struct OptBool { union { bool b; char c; }; OptBool() : c(2) { } OptBool(bool b) : b(b) { } auto has_value() const -> bool { return c != 2; } auto operator*() -> bool& { return b; } };
但是,我并不相信。也就是说,
has_value()
看起来并不安全,因为如果 bool
是活动联合成员,那么 c != 2
会访问非活动成员并执行联合类型双关。据我所知,这在 C++ 中是不允许的。
作者解释说这是无法完成的,因为正在读取一个不活跃的联合成员,并提供了以下实现:
constexpr auto has_value() const -> bool { if consteval { return std::is_within_lifetime(&b); } else { return c != 2; } }
作者这句话的意思是什么?这是否意味着您无法读取常量表达式中的非活动联合成员,但否则会被允许? 该代码是否完全格式良好,或者是否依赖于允许在运行时进行联合类型双关的编译器扩展?
注意:这是作者对可选
我假设
operator*
像往常一样有一个先决条件,即已使用 OptBool(bool b)
重载。当可选值为空时使用 operator*
显然是 UB,但也不是预期用途。
当
b
是活动成员时,访问 c
具有未定义的行为,因为它必须超出生命周期。
这里的目的是查看对象表示,这可以通过添加看似不必要的强制转换来实现:
return *reinterpret_cast<unsigned char*>(reinterpret_cast<OptBool*>(&c)) != 2;
内部转换将产生一个指向
OptBool
对象的指针,因为 OptBool
是标准布局并且可以与 c
子对象进行指针相互转换。
然后,外部转换将生成一个指向表达式类型为 OptBool
的
unsigned char*
对象的指针。通过它进行访问并不违反别名。但是,目前尚未指定此访问应读取什么值。其目的是读取
OptBool
对象(以及
bool
或
char
对象)的对象表示的第一个字节,但目前尚未指定会发生这种情况。有P1839 试图解决这个问题。实际上,每个人都认为这是一种行为,即使标准目前没有这么说,这是一个缺陷。 在任何情况下,当然的实现都假设
bool
的具体实现,特别是它的大小、对齐方式和对象/值表示。