非默认运算符 <=> 在 C++20 中不生成 == 和 !=
non-defaulted operator <=> doesn't generate == and != in C++20
我 运行 对 C++20 中的新宇宙飞船运算符 <=>
感到奇怪。我正在使用 Visual Studio 2019 编译器和 /std:c++latest
.
这段代码编译正常,正如预期的那样:
#include <compare>
struct X
{
int Dummy = 0;
auto operator<=>(const X&) const = default; // Default implementation
};
int main()
{
X a, b;
a == b; // OK!
return 0;
}
但是,如果我将 X 更改为:
struct X
{
int Dummy = 0;
auto operator<=>(const X& other) const
{
return Dummy <=> other.Dummy;
}
};
我收到以下编译器错误:
error C2676: binary '==': 'X' does not define this operator or a conversion to a type acceptable to the predefined operator
我也在 clang 上试过这个,我得到了类似的行为。
我希望能解释一下为什么默认实现正确生成 operator==
,而自定义实现却不能。
在此功能的标准化过程中,决定在逻辑上将相等性和排序分开。因此,等式测试(==
和 !=
)的使用将 永远不会 调用 operator<=>
。但是,仍然认为能够使用一个声明来默认它们两者是有用的。因此,如果您默认 operator<=>
,则决定您也意味着默认 operator==
(除非您稍后定义它或更早定义它)。
至于why this decision was made,基本的推理是这样的。考虑 std::string
。两个字符串的排序是字典顺序的;每个字符都有其整数值与另一个字符串中的每个字符进行比较。第一个不等式导致排序的结果。
但是,字符串的相等性测试存在短路。如果两个字符串的长度不相等,那么就没有必要进行字符比较;他们不平等。所以如果有人在做相等性测试,如果你能缩短它,你不想做长格式的。
事实证明,许多需要用户定义排序的类型也会提供一些用于相等性测试的短路机制。为了防止人们只实施 operator<=>
而放弃潜在的性能,我们有效地强制每个人都这样做。
这是设计使然。
[class.compare.default] (emphasis mine)
3 If the class definition does not explicitly declare an ==
operator function, but declares a defaulted three-way comparison
operator function, an ==
operator function is declared implicitly
with the same access as the three-way comparison operator function.
The implicitly-declared ==
operator for a class X is an inline
member and is defined as defaulted in the definition of X.
只有默认的 <=>
允许合成的 ==
存在。基本原理是像 std::vector
这样的 class 不应该使用非默认的 <=>
进行相等性测试。使用 <=>
代替 ==
并不是比较向量的最有效方法。 <=>
必须给出准确的顺序,而 ==
可以通过先比较大小来提早放弃。
如果 class 在其三向比较中做了一些特殊的事情,它可能需要在其 ==
中做一些特殊的事情。因此,语言不会生成可能不合理的默认值,而是将其留给程序员。
其他答案很好地解释了为什么语言是这样的。我只是想补充一点,如果它不明显,当然可以让用户提供的 operator<=>
和默认的 operator==
。您只需要显式写入默认的 operator==
:
struct X
{
int Dummy = 0;
auto operator<=>(const X& other) const
{
return Dummy <=> other.Dummy;
}
bool operator==(const X& other) const = default;
};
请注意,默认的 operator==
执行成员 ==
比较。也就是说,并不是按照用户提供的operator<=>
来实现的。因此要求程序员明确要求这是一个次要的安全功能,有助于防止意外。
我 运行 对 C++20 中的新宇宙飞船运算符 <=>
感到奇怪。我正在使用 Visual Studio 2019 编译器和 /std:c++latest
.
这段代码编译正常,正如预期的那样:
#include <compare>
struct X
{
int Dummy = 0;
auto operator<=>(const X&) const = default; // Default implementation
};
int main()
{
X a, b;
a == b; // OK!
return 0;
}
但是,如果我将 X 更改为:
struct X
{
int Dummy = 0;
auto operator<=>(const X& other) const
{
return Dummy <=> other.Dummy;
}
};
我收到以下编译器错误:
error C2676: binary '==': 'X' does not define this operator or a conversion to a type acceptable to the predefined operator
我也在 clang 上试过这个,我得到了类似的行为。
我希望能解释一下为什么默认实现正确生成 operator==
,而自定义实现却不能。
在此功能的标准化过程中,决定在逻辑上将相等性和排序分开。因此,等式测试(==
和 !=
)的使用将 永远不会 调用 operator<=>
。但是,仍然认为能够使用一个声明来默认它们两者是有用的。因此,如果您默认 operator<=>
,则决定您也意味着默认 operator==
(除非您稍后定义它或更早定义它)。
至于why this decision was made,基本的推理是这样的。考虑 std::string
。两个字符串的排序是字典顺序的;每个字符都有其整数值与另一个字符串中的每个字符进行比较。第一个不等式导致排序的结果。
但是,字符串的相等性测试存在短路。如果两个字符串的长度不相等,那么就没有必要进行字符比较;他们不平等。所以如果有人在做相等性测试,如果你能缩短它,你不想做长格式的。
事实证明,许多需要用户定义排序的类型也会提供一些用于相等性测试的短路机制。为了防止人们只实施 operator<=>
而放弃潜在的性能,我们有效地强制每个人都这样做。
这是设计使然。
[class.compare.default] (emphasis mine)
3 If the class definition does not explicitly declare an
==
operator function, but declares a defaulted three-way comparison operator function, an==
operator function is declared implicitly with the same access as the three-way comparison operator function. The implicitly-declared==
operator for a class X is an inline member and is defined as defaulted in the definition of X.
只有默认的 <=>
允许合成的 ==
存在。基本原理是像 std::vector
这样的 class 不应该使用非默认的 <=>
进行相等性测试。使用 <=>
代替 ==
并不是比较向量的最有效方法。 <=>
必须给出准确的顺序,而 ==
可以通过先比较大小来提早放弃。
如果 class 在其三向比较中做了一些特殊的事情,它可能需要在其 ==
中做一些特殊的事情。因此,语言不会生成可能不合理的默认值,而是将其留给程序员。
其他答案很好地解释了为什么语言是这样的。我只是想补充一点,如果它不明显,当然可以让用户提供的 operator<=>
和默认的 operator==
。您只需要显式写入默认的 operator==
:
struct X
{
int Dummy = 0;
auto operator<=>(const X& other) const
{
return Dummy <=> other.Dummy;
}
bool operator==(const X& other) const = default;
};
请注意,默认的 operator==
执行成员 ==
比较。也就是说,并不是按照用户提供的operator<=>
来实现的。因此要求程序员明确要求这是一个次要的安全功能,有助于防止意外。