C++联合成员指针是否应视为不同实体?MSVC编译报错解析
问题:联合成员指针作为模板实参的特化冲突
测试代码
union a { int b; int c; }; template<int a::* p> void b() {} template<> void b< &a::b >() {} template<> void b< &a::c >() {}
编译表现
- GCC和Clang可正常编译通过
- MSVC编译报错:
error C2766: explicit specialization; 'void b<pointer-to-member(0x0)>(void)' has already been defined
名字修饰差异
从编译器的名字修饰结果可见:
- GCC/Clang会编码联合成员的具体名称,因此两个特化的修饰名不同:
_Z1bIXadL_ZN1a1bEEEEvv ^ 对应成员b的标识 _Z1bIXadL_ZN1a1cEEEEvv ^ 对应成员c的标识
- MSVC仅编码成员在联合中的偏移量(此处两个成员偏移量均为0),导致两个特化的修饰名完全一致,被判定为重复定义:
??$b@$0A@@@YAXXZ ^ 仅编码了偏移量0
疑问
这是MSVC的bug,还是属于C++标准中的未定义/未指定行为?是否有相关标准条款可参考?
解答
这是MSVC的bug,不符合C++标准规范。
依据C++标准的相关条款:
- 指向联合成员的指针的唯一性:根据C++标准[expr.unary.op]章节,即使联合中多个成员的内存偏移量相同,
&a::b和&a::c是指向不同成员实体的指针,属于不同的表达式值,二者并不相等。 - 模板特化的区分规则:[temp.spec]章节明确规定,模板特化的唯一性由模板实参的类型和值共同决定。对于指向成员的指针类型的模板实参,只要指向的是不同的成员,就属于不同的实参值,对应的特化是独立的函数实体,编译器不能将其视为重复定义。
MSVC仅通过成员的内存偏移量来编码模板实参,忽略了成员本身的标识,错误地将两个不同的特化判定为重复定义,这是不符合标准的实现缺陷。
内容的提问来源于stack exchange,提问作者Ofek Shilon
相关产品推荐
相关产品推荐

