P2641中基于强制转换的optional<bool>实现是否定义良好?
OptBool实现的合规性分析 针对你提出的三个疑问,结合C++标准规则逐一分析如下:
1. char与bool的别名使用是否被允许?
严格别名规则(C++标准[basic.lval]/11)规定:除非是char、unsigned char或std::byte类型,否则不能用一种类型的左值访问另一种不同类型的对象。但这里的场景需要区分对象生命周期:
当调用OptBool(bool)构造函数时,placement new在c的存储位置创建了bool对象,此时原char对象的生命周期已终止,该内存位置的有效对象变为bool。
但直接通过(bool&)c强制转换的行为仍属于未定义行为:编译器会将c视为char类型的类成员,不会自动识别其存储位置已被bool对象覆盖。这种强制转换绕过了严格别名规则的例外逻辑,无法让编译器正确识别实际访问的对象类型。
2. char生命周期结束后,c本身是否仍有效?
c作为OptBool的非静态数据成员,其名字绑定的是最初构造的char对象。当placement new在该位置创建bool对象后,原char对象的生命周期终止(C++标准[basic.life]/1)。此时c这个名字不再指代任何有效的char对象,也不能自动关联到新的bool对象——成员的类型是编译期确定的,编译器不会将成员名字与内存中后续创建的其他类型对象绑定。
因此,直接用c访问内存中的bool对象不符合标准,c作为char对象已无效,其名字也无法合法指向新对象。
3. 转换为bool&是否需要std::launder?
是的,必须使用std::launder才能合法获取bool对象的引用。原因如下:
- 通过
&c得到的是指向原char对象的指针,但该位置现在存在的是bool对象,且bool与char不属于可互换类型(C++标准[ptr.launder]/1)。 std::launder的作用是告知编译器:该指针指向的内存位置已存在新对象,需要返回指向新对象的有效指针。
修正后的operator*实现应如下:
auto operator*() -> bool& { return *std::launder(reinterpret_cast<bool*>(&c)); }
只有这样才能确保访问的是内存中有效的bool对象,避免未定义行为。
内容的提问来源于stack exchange,提问作者Jan Schultke

