无需重定义与转换,如何复用基类A的非成员比较运算符于派生类B?
代码片段
struct A { int a; }; bool operator==(const A& lhs, const A& rhs) { return lhs.a == rhs.a; } template <typename T> bool operator==(const A& lhs, const T& rhs) { return lhs.a == rhs; } template <typename T> bool operator==(const T& rhs, const A& lhs) { return lhs.a == rhs; } struct B : public A { }; int main() { A a1, a2; B b1, b2; auto c1 = a1 == a2; auto c2 = static_cast<A>(b1) == static_cast<A>(b2); auto c3 = b1 == b2; // 编译失败:存在重载歧义 }
问题描述
现有基类A及其非成员==运算符(含模板版本),派生类B公开继承自A,且无法修改A的代码及运算符。当前b1 == b2无法直接编译,需通过static_cast转换为A类型才能比较。现询问:
- 是否可无需static_cast直接让
b1 == b2生效? - 是否可不为B重定义显式比较运算符,直接复用A的非成员比较运算符?
问题分析
b1 == b2编译失败的核心原因是重载决议歧义:
当比较两个B对象时,编译器会找到三个候选运算符:
- 非模板版本:
bool operator==(const A&, const A&)(需将两个B转换为A,两次用户定义转换) - 模板实例化版本1:
bool operator==(const A&, const B&)(需将左侧B转换为A,一次用户定义转换) - 模板实例化版本2:
bool operator==(const B&, const A&)(需将右侧B转换为A,一次用户定义转换)
由于后两个模板实例化的转换成本相同(均为一次用户定义转换),编译器无法确定优先选择哪一个,因此抛出歧义错误。
解决方案
可以通过特化并删除导致歧义的模板版本,让编译器自动选择原有的非模板operator==(const A&, const A&),无需static_cast,也无需为B显式定义新的比较运算符:
// 在B的定义后添加以下特化代码 template <> bool operator==(const A& lhs, const B& rhs) = delete; template <> bool operator==(const B& lhs, const A& rhs) = delete;
添加后,b1 == b2的重载决议中,两个导致歧义的模板实例化版本被排除,编译器会自动将两个B对象转换为A,调用原有的非模板比较运算符,实现直接比较。
答案总结
- 可以无需static_cast直接让
b1 == b2生效,通过特化删除歧义模板版本即可。 - 可以直接复用A的非成员比较运算符,无需为B重定义显式比较运算符,上述方案就是复用原有的非模板
operator==(const A&, const A&)。
内容的提问来源于stack exchange,提问作者Juergen
相关产品推荐
相关产品推荐

