简体   繁体   English

当“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{};
}

See online demo在线演示

Why must I provide operator == when operator <=> is enough?operator <=>就足够时,为什么我必须提供operator ==

Why must I provide operator== when operator<=> is enough?operator<=>足够时,为什么我必须提供operator==

Well, mainly because it's not enough :-)嗯,主要是因为这不够:-)

Equality and ordering are different buckets when it comes time for C++ to rewrite your statements:当 C++ 重写你的语句时,相等和排序是不同的

Equality平等 Ordering订购
Primary基本的 == == <=> <=>
Secondary中学 != != <, >, <=, >= <、>、<=、>=

Primary operators have the ability to be reversed, and secondary operators have the ability to be rewritten in terms of their corresponding primary operator:一级算子可以反转,二级算子可以根据对应的一级算子改写

  • reversing means that a == b can be either:反转意味着a == b可以是:
    • a.operator==(b) if available; a.operator==(b)如果可用; or或者
    • b.operator==(a) if not. b.operator==(a)如果不是。
  • rewriting means that a != b can be:重写意味着a != b可以是:
    • ! a.operator==(b) ! a.operator==(b) if available ! a.operator==(b)如果可用

That last one could also be ! b.operator==(a)最后一个也可以! b.operator==(a) ! b.operator==(a) if you have to rewrite and reverse it (I'm not entirely certain of that since my experience has mostly been with the same types being compared). ! b.operator==(a)如果你必须重写反转它(我不完全确定,因为我的经验主要是比较相同的类型)。

But the requirement that rewriting not take place by default across the equality/ordering boundary means that <=> is not a rewrite candidate for == .但是默认情况下不跨等式/排序边界进行重写的要求意味着<=>不是==的重写候选者。


The reason why equality and ordering are separated like that can be found in this P1185 paper , from one of the many standards meetings that discussed this.平等和排序如此分开的原因可以在这篇 P1185 论文中找到, 该论文来自讨论此问题的众多标准会议之一。

There are many scenarios where automatically implementing == in terms of <=> could be quite inefficient.在许多情况下,根据<=>自动实现==可能效率很低。 String, vector, array, or any other collections come to mind.想到字符串、向量、数组或任何其他集合。 You probably don't want to use <=> to check the equality of the two strings:您可能不想使用<=>来检查两个字符串的相等性:

  • "xxxxx(a billion other x's)" ; "xxxxx(a billion other x's)" ; and
  • "xxxxx(a billion other x's)_and_a_bit_more" . "xxxxx(a billion other x's)_and_a_bit_more"

That's because <=> would have to process the entire strings to work out ordering and then check if the ordering was strong-equal.那是因为<=>必须处理整个字符串来计算排序,然后检查排序是否强相等。

But a simple length check upfront would tell you very quickly that they were unequal.但是预先进行简单的长度检查会很快告诉您它们是不相等的。 This is the difference between O(n) time complexity, a billion or so comparisons, and O(1), a near-immediate result.这是 O(n) 时间复杂度(十亿次左右的比较)和 O(1)(近乎立即的结果)之间的差异。


You can always default equality if you know it will be okay (or you're happy to live with any performance hit it may come with).如果您知道这没问题(或者您乐于忍受它可能带来的任何性能损失),您始终可以默认相等。 But it was thought best not to have the compiler make that decision for you.但人们认为最好不要让编译器为您做出决定。

In more detail, consider the following complete program:更详细地,请考虑以下完整程序:

#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";
}

It won't compile as-is since it needs an operator== for the final statement.它不会按原样编译,因为它需要一个operator==作为最终语句。 But you don't have to provide a real one (the first commented-out chunk), you can just tell it to use the default (the second).但是你不必提供一个真正的(第一个注释掉的块),你可以告诉它使用默认值(第二个)。 And, in this case, that's probably the correct decision as there's no real performance impact from using the default.而且,在这种情况下,这可能是正确的决定,因为使用默认值没有真正的性能影响。


Keep in mind that you only need to provide an equality operator if you explicitly provide a three-way comparison operator (and you use == or != , of course).请记住,如果您明确提供三向比较运算符(当然,您使用==!= ,则只需要提供相等运算符。 If you provide neither, C++ will give you both defaults.如果你都没有提供,C++ 会给你两个默认值。

And, even though you have to provide two functions (with one possibly being a defaulted one), it's still better than previously, where you had to explicitly provide them all, something like:而且,即使您必须提供两个函数(其中一个可能是默认函数),它仍然比以前更好,以前您必须明确提供它们例如:

  • a == b . a == b
  • a < b . a < b
  • a != b , defined as ! (a == b) a != b ,定义为! (a == b) ! (a == b) . ! (a == b)
  • a > b , defined as ! (a < b || a == b) a > b ,定义为! (a < b || a == b) ! (a < b || a == b) . ! (a < b || a == b)
  • a <= b , defined as a < b || a == b a <= b ,定义为a < b || a == b a < b || a == b . a < b || a == b
  • a >= b , defined as ! (a < b) a >= b ,定义为! (a < b) ! (a < b) . ! (a < b)

Why must I provide 'operator ==' when 'operator <=>' is enough?当“operator <=>”就足够了时,为什么我必须提供“operator ==”?

Because it won't be used.因为不会用。

It will be enough if you were to use the defaulted one:如果您要使用默认值就足够了:

struct A
{
    int n;
    auto operator<=>(A const& other) const = default;
};

Basically, n == n is potentially more efficient than (a <=> a) == std::strong_ordering::equal and there are many cases where that is an option.基本上, n == n可能比(a <=> a) == std::strong_ordering::equal更有效,并且在很多情况下这是一种选择。 When you provide a user defined <=> , the language implementation cannot know whether latter could be substituted with the former, nor can it know whether latter is unnecessarily inefficient.当您提供用户定义的<=> ,语言实现无法知道后者是否可以替换为前者,也无法知道后者是否不必要地低效。

So, if you need a custom three way comparison, then you need a custom equality comparison.所以,如果你需要一个自定义的三向比较,那么你需要一个自定义的相等比较。 The example class doesn't need a custom three way comparison, so you should use the default one.示例类不需要自定义的三向比较,因此您应该使用默认的比较。

Looking at the previous answers, nobody has addressed another issue: For ordering purposes, two objects might be equivalent and yet not be equal.查看之前的答案,没有人解决另一个问题:出于排序目的,两个对象可能相等但不相等。 For example, I might want to sort on strings in Unicode NFC normalization with case-folding to lowercase, but for equality testing, I want to verify that the strings are actually identical, with case being significant and perhaps even distinguishing between é and ´ + e in the input.例如,我可能想对 Unicode NFC 规范化中的字符串进行排序,并将大小写转换为小写,但对于相等性测试,我想验证字符串实际上是否相同,大小写很重要,甚至可能区分 é 和 ´ + e 在输入中。

Yes, this is a somewhat contrived example, but it serves to make the point that the definition of <=> does not require strong ordering so you cannot rely on <=> even potentially returning std::strong_ordering::equal .是的,这是一个有点人为的示例,但它表明<=>的定义不需要排序,因此您不能依赖<=>甚至可能返回std::strong_ordering::equal Making == default to <=> returns std::strong_ordering::equal cannot be assumed to be a valid implementation.使==默认为<=>返回std::strong_ordering::equal不能假定为有效实现。

Because == can sometimes be implemented faster than using a <=> b == 0 , so the compiler refuses to use potentially suboptimal implementation by default.因为==有时可以比使用a <=> b == 0更快地实现,所以默认情况下编译器拒绝使用潜在的次优实现。

Eg consider std::string , which can check if sizes are the same before looping over the elements.例如考虑std::string ,它可以在循环元素之前检查大小是否相同。

Note that you don't have to implement == manually.请注意,您不必手动实现== You can =default it, which will implement it in terms of <=> .您可以=default it,它将根据<=>实现它。

Also note that if you =default <=> itself, then =default ing == is not necessary.另请注意,如果您=default <=>本身,则不需要=default ing ==

No, you don't.不,你没有。 Just add只需添加

  bool operator==(A const& other) const = default;

https://godbolt.org/z/v1WGhxca6 https://godbolt.org/z/v1WGhxca6

You can always overload them to different semantics.您总是可以将它们重载为不同的语义。 To prevent unexpected auto generated function, the = default is needed为了防止意外的自动生成函数,需要= default

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么:: operator new就足够了,为什么:: operator new []是必要的? - Why is ::operator new[] necessary when ::operator new is enough? 什么时候必须重载运算符&lt;&lt;? - When must I overload operator <<? 为什么必须在运算符重载中提供关键字const - why must you provide the keyword const in operator overloads 为什么在表达式解析器中`TokPrec &lt; NextPrec` 时运算符优先级必须增加 1? - Why operator precedence must be increased by 1 when `TokPrec < NextPrec` in expression parser? 为什么boost :: variant不提供运算符!= - Why does boost::variant not provide operator != 为什么在我声明时“不匹配&#39;operator&lt;&#39;”? - Why “no match for 'operator<'” when I declared it? 比较地址足够这个`operator ==`? - Is comparing addresses enough for this `operator==`? 当在 class 中重载运算符 &lt;&lt; 时,为什么我必须将其声明为朋友 function,尽管它已经在 class 中声明? - When overloading operator << inside a class, why I must declare it as a friend function despite the fact that it is already declared inside the class? 为什么 C# 运算符重载必须是静态的? - Why must C# operator overloads be static? 为什么重载运算符<<必须通过引用返回? - why overloading of operator<< must return by reference?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM