C++20中operator==歧义问题:代码二义性成因及友元解法疑问
C++20中CRTP结构下operator==的二义性解析
先看引发问题的代码:
template <typename T> struct base_widget { bool operator==(T const&) const; }; struct gadget : base_widget<gadget> {}; bool foo(gadget const& a, gadget const& b) { return a == b; }
为什么合成候选会引发二义性?
C++20对相等运算符的重载决议新增规则:对于每个非重写的候选运算符,会自动生成参数顺序反转的合成候选。在这个CRTP场景中:
- 原始候选是
base_widget<gadget>的成员函数bool operator==(gadget const&) const,调用时this指针的静态类型是base_widget<gadget> const&,需要将派生类gadget隐式转换为基类类型。 - 根据规则,针对这个成员候选会生成合成的非成员候选:
bool operator==(gadget const&, base_widget<gadget> const&)。
当解析a == b时,两个候选的匹配成本完全一致:
- 调用原始成员候选:需要将
a(gadget const&)转换为base_widget<gadget> const&,参数b直接匹配。 - 调用合成候选:需要将
b(gadget const&)转换为base_widget<gadget> const&,参数a直接匹配。
这两个转换都是派生类到基类的标准转换,优先级相同,重载决议无法区分,因此产生二义性。
为什么声明为友元能解决问题?
将operator==声明为base_widget的友元后,该运算符会成为非成员函数,结合CRTP特性可以定义出参数类型完全对称的版本:
template <typename T> struct base_widget { friend bool operator==(const T& lhs, const T& rhs) { // 实现比较逻辑,可通过static_cast访问基类成员 return static_cast<const base_widget&>(lhs) == static_cast<const base_widget&>(rhs); } };
此时:
- 针对
gadget实例化后的operator==签名为bool operator==(const gadget&, const gadget&),对表达式a == b是精确匹配,无需任何类型转换。 - C++20生成的反转合成候选与原始候选签名完全一致,不会新增额外候选。
重载决议会直接选中这个精确匹配的函数,二义性自然消除。
内容的提问来源于stack exchange,提问作者Jaka
相关产品推荐
相关产品推荐

