C++父类无ref-qualifier成员函数被子类带ref-qualifier版本重载,哪个编译器正确?
结论
Clang的行为完全符合C++标准规定,GCC在此处存在实现偏差,属于非标准扩展行为。
规则解释
1. 同类重载的限制
C++标准明确规定,同一类中不能同时定义带ref-qualifier和不带ref-qualifier的同名成员函数,因为二者重载会存在固有歧义:
- 不带ref-qualifier的成员函数,隐式对象参数既可以接受左值对象,也可以接受右值对象
- 带
&限定的成员函数只能接受左值对象,带&&限定的只能接受右值对象
二者在左值调用场景下候选优先级相同,直接定义会报重定义错误。
2. 继承场景的重载有效性
示例中通过using A::f引入父类函数,再在子类定义带ref-qualifier的同名函数是合法的:using声明只是将父类的函数名引入子类作用域,不属于在子类中重复定义,因此不会触发同类重载的报错。
3. 调用时的歧义来源
对于b.f()调用,b是B类型的左值,此时重载候选有两个:
- 从父类引入的
void A::f():无ref限定,隐式对象参数需要从B&转换为A&,属于派生类到基类的精确匹配转换 - 子类自定义的
void B::f() &:带左值ref限定,隐式对象参数为B&,属于完全匹配
C++标准中,派生类到基类的左值引用转换属于和完全匹配同等级的精确匹配序列,且ref-qualifier的决胜规则仅适用于隐式对象参数底层类型完全一致的场景,此处两个候选的隐式对象参数类型分别为A&和B&,不满足决胜规则的适用条件,因此两个候选优先级完全相同,调用属于歧义,编译器应当报错。
示例代码
struct A { void f() {} //void f() & {} // 同类定义会直接报重定义错误 }; struct B : A { using A::f; void f() & {} // 继承场景下允许定义 }; int main() { B b; b.f(); // 标准规定此处属于歧义调用,应当报错 }
内容的提问来源于stack exchange,提问作者Fedor
相关产品推荐
相关产品推荐

