C++20 比较:关于不明确的反向运算符的警告
C++20 comparison: warning about ambiguous reversed operator
考虑这个有效的 C++17 示例:
struct A {
bool operator==(const A&);
};
int main() {
return A{} == A{};
}
当compiled in clang with -std=c++20 it gives:
<source>:7:15: warning: ISO C++20 considers use of overloaded operator '==' (with operand types 'A' and 'A') to be ambiguous despite there being a unique best viable function [-Wambiguous-reversed-operator]
return A{} == A{};
~~~ ^ ~~~
<source>:2:9: note: ambiguity is between a regular call to this operator and a call with the argument order reversed
bool operator==(const A&);
这个警告是否意味着 C++20 不允许使用典型的比较运算符来比较两个相同类型的对象?什么是正确的选择?预计未来的草稿中情况会有所改变吗?
Does this warning mean that C++20 disallows using a typical comparison operator to compare two objects of the same type? What is the correct alternative? Is the situation expected to change in future drafts?
这实际上不是典型的比较运算符,它已经有点错误了 - 因为它只允许在一侧使用 const
对象(您的类型 A
不满足新的 equality_comparable
概念,即使没有任何语言变化)。
你必须这样写:
struct A {
bool operator==(const A&) const;
// ^^^^^^
};
这是 C++20 的最终规则。
具体问题是,在 C++20 中,比较运算符添加了重写和反转候选的新概念。所以查找表达式 a == b
也将最终匹配像 b == a
这样的运算符。在典型情况下,这意味着您必须编写更少的运算符,因为我们知道相等性是可交换的。
但是如果你有一个常量不匹配,你最终会得到这两个候选者:
bool operator==(/* this*/ A&, A const&); // member function
bool operator==(A const&, /* this*/ A&); // reversed member function
有两个 A
类型的参数。第一个候选者在第一个参数中更好,第二个候选者在第二个参数中更好。两个候选人都不比另一个好,因此模棱两可。
重载决策的一般规则是每个参数类型必须分开 至少与所选函数的参数类型接近于任何其他函数的参数类型:
struct A {A(int);};
void f(long,int); // #1
void f(int,A); // #2
void g() {f(0,0);} // error: ambiguous
#2 第二个参数的更糟糕的转换并不能弥补第一个参数的 int
→long
转换。
在 C++20 中,添加了各种重写规则以避免编写这么多完全相同的比较运算符重载。虽然手写的“反向候选”和相同的编译器生成的之间的微不足道的歧义由 tie-breaker 更喜欢实函数的规则处理,但这(再次)不足以弥补任何论点的更糟糕的转换。
根据公认的 (C++17) 实践仔细编写的比较运算符很少 运行 与此冲突,但是 有问题的 像这样的签名(具有不对称 const
) 很可能会有问题(以新的方式)。希望发现的错误多于此扩展引起的错误。
考虑这个有效的 C++17 示例:
struct A {
bool operator==(const A&);
};
int main() {
return A{} == A{};
}
当compiled in clang with -std=c++20 it gives:
<source>:7:15: warning: ISO C++20 considers use of overloaded operator '==' (with operand types 'A' and 'A') to be ambiguous despite there being a unique best viable function [-Wambiguous-reversed-operator]
return A{} == A{};
~~~ ^ ~~~
<source>:2:9: note: ambiguity is between a regular call to this operator and a call with the argument order reversed
bool operator==(const A&);
这个警告是否意味着 C++20 不允许使用典型的比较运算符来比较两个相同类型的对象?什么是正确的选择?预计未来的草稿中情况会有所改变吗?
Does this warning mean that C++20 disallows using a typical comparison operator to compare two objects of the same type? What is the correct alternative? Is the situation expected to change in future drafts?
这实际上不是典型的比较运算符,它已经有点错误了 - 因为它只允许在一侧使用 const
对象(您的类型 A
不满足新的 equality_comparable
概念,即使没有任何语言变化)。
你必须这样写:
struct A {
bool operator==(const A&) const;
// ^^^^^^
};
这是 C++20 的最终规则。
具体问题是,在 C++20 中,比较运算符添加了重写和反转候选的新概念。所以查找表达式 a == b
也将最终匹配像 b == a
这样的运算符。在典型情况下,这意味着您必须编写更少的运算符,因为我们知道相等性是可交换的。
但是如果你有一个常量不匹配,你最终会得到这两个候选者:
bool operator==(/* this*/ A&, A const&); // member function
bool operator==(A const&, /* this*/ A&); // reversed member function
有两个 A
类型的参数。第一个候选者在第一个参数中更好,第二个候选者在第二个参数中更好。两个候选人都不比另一个好,因此模棱两可。
重载决策的一般规则是每个参数类型必须分开 至少与所选函数的参数类型接近于任何其他函数的参数类型:
struct A {A(int);};
void f(long,int); // #1
void f(int,A); // #2
void g() {f(0,0);} // error: ambiguous
#2 第二个参数的更糟糕的转换并不能弥补第一个参数的 int
→long
转换。
在 C++20 中,添加了各种重写规则以避免编写这么多完全相同的比较运算符重载。虽然手写的“反向候选”和相同的编译器生成的之间的微不足道的歧义由 tie-breaker 更喜欢实函数的规则处理,但这(再次)不足以弥补任何论点的更糟糕的转换。
根据公认的 (C++17) 实践仔细编写的比较运算符很少 运行 与此冲突,但是 有问题的 像这样的签名(具有不对称 const
) 很可能会有问题(以新的方式)。希望发现的错误多于此扩展引起的错误。