簡體   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{};
}

在線演示

operator <=>就足夠時,為什么我必須提供operator ==

operator<=>足夠時,為什么我必須提供operator==

嗯,主要是因為這不夠:-)

當 C++ 重寫你的語句時,相等和排序是不同的

平等 訂購
基本的 == <=>
中學 != <、>、<=、>=

一級算子可以反轉,二級算子可以根據對應的一級算子改寫

  • 反轉意味着a == b可以是:
    • a.operator==(b)如果可用; 或者
    • b.operator==(a)如果不是。
  • 重寫意味着a != b可以是:
    • ! a.operator==(b) ! a.operator==(b)如果可用

最后一個也可以! b.operator==(a) ! b.operator==(a)如果你必須重寫反轉它(我不完全確定,因為我的經驗主要是比較相同的類型)。

但是默認情況下不跨等式/排序邊界進行重寫的要求意味着<=>不是==的重寫候選者。


平等和排序如此分開的原因可以在這篇 P1185 論文中找到, 該論文來自討論此問題的眾多標准會議之一。

在許多情況下,根據<=>自動實現==可能效率很低。 想到字符串、向量、數組或任何其他集合。 您可能不想使用<=>來檢查兩個字符串的相等性:

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

那是因為<=>必須處理整個字符串來計算排序,然后檢查排序是否強相等。

但是預先進行簡單的長度檢查會很快告訴您它們是不相等的。 這是 O(n) 時間復雜度(十億次左右的比較)和 O(1)(近乎立即的結果)之間的差異。


如果您知道這沒問題(或者您樂於忍受它可能帶來的任何性能損失),您始終可以默認相等。 但人們認為最好不要讓編譯器為您做出決定。

更詳細地,請考慮以下完整程序:

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

它不會按原樣編譯,因為它需要一個operator==作為最終語句。 但是你不必提供一個真正的(第一個注釋掉的塊),你可以告訴它使用默認值(第二個)。 而且,在這種情況下,這可能是正確的決定,因為使用默認值沒有真正的性能影響。


請記住,如果您明確提供三向比較運算符(當然,您使用==!= ,則只需要提供相等運算符。 如果你都沒有提供,C++ 會給你兩個默認值。

而且,即使您必須提供兩個函數(其中一個可能是默認函數),它仍然比以前更好,以前您必須明確提供它們例如:

  • a == b
  • a < b
  • a != b ,定義為! (a == b) ! (a == b)
  • a > b ,定義為! (a < b || a == b) ! (a < b || a == b)
  • a <= b ,定義為a < b || a == b a < b || a == b
  • a >= b ,定義為! (a < b) ! (a < b)

當“operator <=>”就足夠了時,為什么我必須提供“operator ==”?

因為不會用。

如果您要使用默認值就足夠了:

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

基本上, n == n可能比(a <=> a) == std::strong_ordering::equal更有效,並且在很多情況下這是一種選擇。 當您提供用戶定義的<=> ,語言實現無法知道后者是否可以替換為前者,也無法知道后者是否不必要地低效。

所以,如果你需要一個自定義的三向比較,那么你需要一個自定義的相等比較。 示例類不需要自定義的三向比較,因此您應該使用默認的比較。

查看之前的答案,沒有人解決另一個問題:出於排序目的,兩個對象可能相等但不相等。 例如,我可能想對 Unicode NFC 規范化中的字符串進行排序,並將大小寫轉換為小寫,但對於相等性測試,我想驗證字符串實際上是否相同,大小寫很重要,甚至可能區分 é 和 ´ + e 在輸入中。

是的,這是一個有點人為的示例,但它表明<=>的定義不需要排序,因此您不能依賴<=>甚至可能返回std::strong_ordering::equal 使==默認為<=>返回std::strong_ordering::equal不能假定為有效實現。

因為==有時可以比使用a <=> b == 0更快地實現,所以默認情況下編譯器拒絕使用潛在的次優實現。

例如考慮std::string ,它可以在循環元素之前檢查大小是否相同。

請注意,您不必手動實現== 您可以=default it,它將根據<=>實現它。

另請注意,如果您=default <=>本身,則不需要=default ing ==

不,你沒有。 只需添加

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

https://godbolt.org/z/v1WGhxca6

您總是可以將它們重載為不同的語義。 為了防止意外的自動生成函數,需要= default

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM