C++中能否通过成员指针语法激活union成员?编译器行为辨析
关于C++中成员指针语法激活union成员的标准合规性与编译器行为分析
背景规则
C++标准规定,union同一时刻仅能存在0个或1个活跃成员,仅明确列举了少数激活成员的合法方式,比如u.x = 3;这类直接通过成员访问运算符(./->)进行的赋值操作。在constexpr或consteval语境下必须严格遵守该规则,否则编译器会因违反对象生命周期规则报错。
测试代码
为验证成员指针语法(如u.*member = 3;)能否激活union成员,编写了如下C++20测试代码:
union U { int x; int y; }; template<auto member = &U::x> constexpr void activate(U& u) noexcept { u.*member = 3; } constexpr int get(U& u) noexcept { return u.x; } constexpr int test() noexcept { U u; activate(u); return get(u); } static constexpr int const result{test()}; int main() { return result; }
编译器测试结果
在MSVC 19、GCC 13、Clang 16三个编译器上的测试表现如下:
- MSVC 19:接受原代码并生成正确汇编;若将模板参数改为
&U::y,会诊断激活错误成员的问题;非constexpr语境下接受代码。 - GCC 13:接受原代码并生成正确汇编;仅当
y的类型改为char时,才会触发激活错误成员的诊断;非constexpr语境下接受代码。 - Clang 16:始终拒绝通过成员指针语法激活union成员;非
constexpr语境下接受代码。
标准合规性与编译器正确性分析
标准是否允许该操作?
C++标准并未明确将成员指针解引用后的赋值操作列为union成员的合法激活方式,仅明确认可直接通过./->访问成员的赋值行为。但从语义上,u.*member = 3与直接成员赋值的逻辑完全等价——都是对union的目标成员进行赋值。
哪个编译器行为正确?
- Clang 16的行为更严格贴合标准的明确规定:由于标准未明确允许成员指针语法激活成员,因此在
constexpr语境下拒绝该代码,属于严格遵循标准文本的表现。 - MSVC 19和GCC 13则属于对标准进行了语义扩展:将成员指针解引用后的赋值视为与直接成员赋值等价的操作,允许其激活union成员,这种实现更贴合代码的语义意图,但超出了标准明确列举的合法激活方式范围。
在非constexpr语境下,三者均接受代码的原因是:该场景下的生命周期违规属于未定义行为,编译器可选择不进行诊断。
内容的提问来源于stack exchange,提问作者LB--
相关产品推荐
相关产品推荐

