为什么我必须提供 'operator ==',而 'operator <=>' 就足够了?
Why must I provide 'operator ==' when 'operator <=>' is enough?
#include <compare>
struct A
{
int n;
auto operator<=>(A const& other) const
{
if (n < other.n)
{
return std::strong_ordering::less;
}
else if (n > other.n)
{
return std::strong_ordering::greater;
}
else
{
return std::strong_ordering::equal;
}
}
// compile error if the following code is commented out.
// bool operator==(A const& other) const
// { return n == other.n; }
};
int main()
{
A{} == A{};
}
为什么我必须提供 operator ==
什么时候 operator <=>
就足够了?
因为 ==
有时可以比使用 a <=> b == 0
更快地实现,所以默认情况下编译器拒绝使用潜在的次优实现。
例如考虑 std::string
,它可以在遍历元素之前检查大小是否相同。
请注意,您不必手动实施 ==
。你可以 =default
它,它将根据 <=>
.
来实现它
另请注意,如果您 =default
<=>
本身,则 =default
ing ==
是没有必要的。
不,你不知道。
只需添加
bool operator==(A const& other) const = default;
https://godbolt.org/z/v1WGhxca6
您始终可以将它们重载为不同的语义。为了防止意外的自动生成函数,需要= default
Why must I provide operator==
when operator<=>
is enough?
嗯,主要是因为不够:-)
当 C++ 重写您的语句时,相等性和顺序是 不同的 桶:
Equality
Ordering
Primary
==
<=>
Secondary
!=
<, >, <=, >=
初级运算符可以被反转,二级运算符可以被重写根据其对应的初级运算符:
- 反转意味着
a == b
可以是:
a.operator==(b)
如果可用;或
b.operator==(a)
如果没有。
- 重写意味着
a != b
可以是:
! a.operator==(b)
如果可用
最后一个也可以是 ! b.operator==(a)
如果你必须重写 和 反转它(我不完全确定,因为我的经验主要是正在比较的相同类型)。
但是默认情况下不跨 equality/ordering 边界进行重写的要求意味着 <=>
不是 [=20= 的重写候选者].
原因为什么等式和排序如此分开可以在 this P1185 paper 中找到,来自讨论这个问题的许多标准会议之一。
在很多情况下,根据 <=>
自动实施 ==
可能效率很低。想到字符串、向量、数组或任何其他 collections。您可能不想使用 <=>
来检查两个字符串的相等性:
"xxxxx(a billion other x's)"
;和
"xxxxx(a billion other x's)_and_a_bit_more"
.
那是因为 <=>
必须处理 整个 字符串才能计算出排序,然后检查排序是否为 strong-equal。
但是预先进行简单的长度检查会很快告诉您它们是不相等的。这是 O(n) 时间复杂度(十亿次左右的比较)和 O(1)(near-immediate 结果之间的差异。
如果您知道它没问题(或者您乐于忍受它可能带来的任何性能损失),您始终可以默认 相等。但人们认为最好不要让编译器 为您做出 那个决定。
更详细地,考虑以下完整程序:
#include <iostream>
#include <compare>
class xyzzy {
public:
xyzzy(int data) : n(data) { }
auto operator<=>(xyzzy const &other) const {
// Could probably just use: 'return n <=> other.n;'
// but this is from the OPs actual code, so I didn't
// want to change it too much (formatting only).
if (n < other.n) return std::strong_ordering::less;
if (n > other.n) return std::strong_ordering::greater;
return std::strong_ordering::equal;
}
//auto operator==(xyzzy const &other) const {
// return n == other.n;
//}
//bool operator==(xyzzy const &) const = default;
private:
int n;
};
int main() {
xyzzy twisty(3);
xyzzy passages(3);
if (twisty < passages) std::cout << "less\n";
if (twisty == passages) std::cout << "equal\n";
}
它不会编译 as-is 因为它需要一个 operator==
作为最终语句。但是你不必提供 real 一个(第一个 commented-out 块),你可以告诉它使用默认值(第二个)。而且,在这种情况下,这可能是正确的决定,因为使用默认设置不会对性能产生实际影响。
请记住,如果您 显式 提供 three-way 比较运算符(并且您使用 ==
或 !=
,当然)。如果两者都不提供,C++ 将为您提供这两个默认值。
而且,即使您必须提供两个功能(其中一个可能是默认功能),它仍然比以前更好,您必须明确提供它们 all,类似于:
a == b
.
a < b
.
a != b
,定义为! (a == b)
.
a > b
,定义为! (a < b || a == b)
.
a <= b
,定义为a < b || a == b
.
a >= b
,定义为! (a < b)
.
Why must I provide 'operator ==' when 'operator <=>' is enough?
因为不会用到
如果你使用默认的就足够了:
struct A
{
int n;
auto operator<=>(A const& other) const = default;
};
基本上,n == n
可能比 (a <=> a) == std::strong_ordering::equal
更有效,在很多情况下这是一个选项。当您提供用户定义的 <=>
时,语言实现无法知道后者是否可以替换为前者,也无法知道后者是否不必要地低效。
因此,如果您需要自定义三向比较,则需要自定义相等比较。示例 class 不需要自定义三向比较,因此您应该使用默认的。
查看之前的答案,没有人解决另一个问题:出于排序目的,两个对象可能相等但不相等。例如,我可能想在 Unicode NFC 规范化中对字符串进行排序,将大小写折叠为小写,但对于相等性测试,我想验证字符串实际上是否相同,大小写很重要,甚至可能区分 é 和 ´ + e 在输入中。
是的,这是一个有点做作的例子,但它表明 <=>
的定义不需要 strong 排序,所以你不能依赖<=>
甚至可能返回 std::strong_ordering::equal
。将 ==
默认设置为 <=>
returns std::strong_ordering::equal
不能被认为是有效的实现。
#include <compare>
struct A
{
int n;
auto operator<=>(A const& other) const
{
if (n < other.n)
{
return std::strong_ordering::less;
}
else if (n > other.n)
{
return std::strong_ordering::greater;
}
else
{
return std::strong_ordering::equal;
}
}
// compile error if the following code is commented out.
// bool operator==(A const& other) const
// { return n == other.n; }
};
int main()
{
A{} == A{};
}
为什么我必须提供 operator ==
什么时候 operator <=>
就足够了?
因为 ==
有时可以比使用 a <=> b == 0
更快地实现,所以默认情况下编译器拒绝使用潜在的次优实现。
例如考虑 std::string
,它可以在遍历元素之前检查大小是否相同。
请注意,您不必手动实施 ==
。你可以 =default
它,它将根据 <=>
.
另请注意,如果您 =default
<=>
本身,则 =default
ing ==
是没有必要的。
不,你不知道。 只需添加
bool operator==(A const& other) const = default;
https://godbolt.org/z/v1WGhxca6
您始终可以将它们重载为不同的语义。为了防止意外的自动生成函数,需要= default
Why must I provide
operator==
whenoperator<=>
is enough?
嗯,主要是因为不够:-)
当 C++ 重写您的语句时,相等性和顺序是 不同的 桶:
Equality | Ordering | |
---|---|---|
Primary | == | <=> |
Secondary | != | <, >, <=, >= |
初级运算符可以被反转,二级运算符可以被重写根据其对应的初级运算符:
- 反转意味着
a == b
可以是:a.operator==(b)
如果可用;或b.operator==(a)
如果没有。
- 重写意味着
a != b
可以是:! a.operator==(b)
如果可用
最后一个也可以是 ! b.operator==(a)
如果你必须重写 和 反转它(我不完全确定,因为我的经验主要是正在比较的相同类型)。
但是默认情况下不跨 equality/ordering 边界进行重写的要求意味着 <=>
不是 [=20= 的重写候选者].
原因为什么等式和排序如此分开可以在 this P1185 paper 中找到,来自讨论这个问题的许多标准会议之一。
在很多情况下,根据 <=>
自动实施 ==
可能效率很低。想到字符串、向量、数组或任何其他 collections。您可能不想使用 <=>
来检查两个字符串的相等性:
"xxxxx(a billion other x's)"
;和"xxxxx(a billion other x's)_and_a_bit_more"
.
那是因为 <=>
必须处理 整个 字符串才能计算出排序,然后检查排序是否为 strong-equal。
但是预先进行简单的长度检查会很快告诉您它们是不相等的。这是 O(n) 时间复杂度(十亿次左右的比较)和 O(1)(near-immediate 结果之间的差异。
如果您知道它没问题(或者您乐于忍受它可能带来的任何性能损失),您始终可以默认 相等。但人们认为最好不要让编译器 为您做出 那个决定。
更详细地,考虑以下完整程序:
#include <iostream>
#include <compare>
class xyzzy {
public:
xyzzy(int data) : n(data) { }
auto operator<=>(xyzzy const &other) const {
// Could probably just use: 'return n <=> other.n;'
// but this is from the OPs actual code, so I didn't
// want to change it too much (formatting only).
if (n < other.n) return std::strong_ordering::less;
if (n > other.n) return std::strong_ordering::greater;
return std::strong_ordering::equal;
}
//auto operator==(xyzzy const &other) const {
// return n == other.n;
//}
//bool operator==(xyzzy const &) const = default;
private:
int n;
};
int main() {
xyzzy twisty(3);
xyzzy passages(3);
if (twisty < passages) std::cout << "less\n";
if (twisty == passages) std::cout << "equal\n";
}
它不会编译 as-is 因为它需要一个 operator==
作为最终语句。但是你不必提供 real 一个(第一个 commented-out 块),你可以告诉它使用默认值(第二个)。而且,在这种情况下,这可能是正确的决定,因为使用默认设置不会对性能产生实际影响。
请记住,如果您 显式 提供 three-way 比较运算符(并且您使用 ==
或 !=
,当然)。如果两者都不提供,C++ 将为您提供这两个默认值。
而且,即使您必须提供两个功能(其中一个可能是默认功能),它仍然比以前更好,您必须明确提供它们 all,类似于:
a == b
.a < b
.a != b
,定义为! (a == b)
.a > b
,定义为! (a < b || a == b)
.a <= b
,定义为a < b || a == b
.a >= b
,定义为! (a < b)
.
Why must I provide 'operator ==' when 'operator <=>' is enough?
因为不会用到
如果你使用默认的就足够了:
struct A
{
int n;
auto operator<=>(A const& other) const = default;
};
基本上,n == n
可能比 (a <=> a) == std::strong_ordering::equal
更有效,在很多情况下这是一个选项。当您提供用户定义的 <=>
时,语言实现无法知道后者是否可以替换为前者,也无法知道后者是否不必要地低效。
因此,如果您需要自定义三向比较,则需要自定义相等比较。示例 class 不需要自定义三向比较,因此您应该使用默认的。
查看之前的答案,没有人解决另一个问题:出于排序目的,两个对象可能相等但不相等。例如,我可能想在 Unicode NFC 规范化中对字符串进行排序,将大小写折叠为小写,但对于相等性测试,我想验证字符串实际上是否相同,大小写很重要,甚至可能区分 é 和 ´ + e 在输入中。
是的,这是一个有点做作的例子,但它表明 <=>
的定义不需要 strong 排序,所以你不能依赖<=>
甚至可能返回 std::strong_ordering::equal
。将 ==
默认设置为 <=>
returns std::strong_ordering::equal
不能被认为是有效的实现。