Home Item 29 - 三向比較運算符 <=> (中文)
Post
Cancel

Item 29 - 三向比較運算符 <=> (中文)

三向比較運算符

當我們在寫自己的 struct or class 時,有時候要去該物件的比較, 也就是 <, <=, ==, !=, >=, > 這些 operator。 但寫過的人就知道,上述 6 個 operator 都要去寫非常麻煩。 而且還很容易錯誤。

C++20 引入了三向比較運算符(spaceship operator, <=>),它是一個新的比較工具, 可以統一和簡化比較操作。

三向比較運算符 <=> 的主要功能是: 統一比較運算符:只需要定義一個運算符,即可自動生成所有的比較運算符(<, <=, ==, !=, >=, >)。

code

下面的 code 我們定義一個三維空間中的 Point, 所以會有 x,y,z 的座標。 現在我們來定義這幾個 Point 比大小(先不管物理意義啦)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
#include <compare>

class Point {
public:
    Point(int x, int y, int z) : x(x), y(y), z(z) {}

    // 使用 default 自動生成
    auto operator<=>(const Point& other) const = default;

    // 友元函数聲明
    friend std::ostream& operator<<(std::ostream& os, const Point& point);

private:
    int x, y, z;
};

// 友元函数定義
std::ostream& operator<<(std::ostream& os, const Point& point) {
    os << "Point(" << point.x << ", " << point.y << ", " << point.z << ")";
    return os;
}

auto printHelper(bool result)
{
    if (result)
        return "True";
    else
        return "False";
}

int main() {

    Point p1{ 2, 1, 3 };
    Point p2{ 1, 1, 4 };
    Point p3{ 3, 3, 4 };

    std::cout << p1 << std::endl;
    std::cout << p2 << std::endl;
    std::cout << p3 << std::endl;

    std::cout << "------------" << std::endl;

    // 比較 p1 和 p2
    std::cout << "(p1 == p2): " << printHelper(p1 == p2) << std::endl;
    std::cout << "(p1 < p2): " << printHelper(p1 < p2) << std::endl;
    std::cout << "(p1 > p2): " << printHelper(p1 > p2) << std::endl;

    // 比較 p1 和 p3
    std::cout << "(p1 == p3): " << printHelper(p1 == p3) << std::endl;
    std::cout << "(p1 < p3): " << printHelper(p1 < p3) << std::endl;
    std::cout << "(p1 > p3): " << printHelper(p1 > p3) << std::endl;

    return 0;
}

執行結果 比較順序 x->y->z Desktop View

當你使用 auto operator<=>(const Point& other) const = default 時, 編譯器自動為你生成三向比較運算符, 並按照成員變量的順序進行比較。 這樣做的結果與手動實現的比較邏輯是一致的。

編譯器生成的比較邏輯如下:

  1. 按成員變量聲明的順序依次進行比較。不論成員變量是 publicprotected 還是 private。編譯器會考慮所有成員變量來生成比較邏輯。
  2. 每個成員變量都使用 <=> 進行比較。
  3. 如果某個成員的比較結果不是 equal,則返回該結果。
  4. 如果所有成員都相等,則返回 equal。

手動定義三向比較運算符

當你想要自己定義三向比較運算符時,譬如你覺得比較順序要使用 z -> y -> x

1
2
3
4
5
6
7
8
9
10
11
12
 // 手動定義三向比較運算符
 auto operator<=>(const Point& other) const {
     if (auto cmp = z <=> other.z; cmp != 0) {
         return cmp;
     }

     if (auto cmp = y <=> other.y; cmp != 0) {
         return cmp;
     }

     return x <=> other.x;
 }

這時候 complier 報錯了。

Desktop View

這個錯誤是因為當你定義了三向比較運算符 <=> 時,編譯器期望你也提供等於運算符 == 的定義。雖然 <=> 可以生成 <、<=、> 和 >=,但它不會自動生成 == 和 !=。

你需要手動定義 == 運算符。

1
2
3
4
 // 手動定義等於運算符
 bool operator==(const Point& other) const {
     return x == other.x && y == other.y && z == other.z;
 }

比較順序 z->y->x

Desktop View

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <iostream>
#include <compare>

class Point {
public:
    Point(int x, int y, int z) : x(x), y(y), z(z) {}

    // 手動定義三向比較運算符
    auto operator<=>(const Point& other) const {
        if (auto cmp = z <=> other.z; cmp != 0) {
            return cmp;
        }

        if (auto cmp = y <=> other.y; cmp != 0) {
            return cmp;
        }

        return x <=> other.x;
    }


    // 手動定義等於運算符
    bool operator==(const Point& other) const {
        return x == other.x && y == other.y && z == other.z;
    }

    // 友元函数聲明
    friend std::ostream& operator<<(std::ostream& os, const Point& point);

private:
    int x, y, z;
};

// 友元函数定義
std::ostream& operator<<(std::ostream& os, const Point& point) {
    os << "Point(" << point.x << ", " << point.y << ", " << point.z << ")";
    return os;
}

auto printHelper(bool result)
{
    if (result)
        return "True";
    else
        return "False";
}

int main() {

    Point p1{ 2, 1, 3 };
    Point p2{ 1, 1, 4 };
    Point p3{ 3, 3, 4 };

    std::cout << p1 << std::endl;
    std::cout << p2 << std::endl;
    std::cout << p3 << std::endl;

    std::cout << "------------" << std::endl;

    // 比較 p1 和 p2
    std::cout << "(p1 == p2): " << printHelper(p1 == p2) << std::endl;
    std::cout << "(p1 < p2): " << printHelper(p1 < p2) << std::endl;
    std::cout << "(p1 > p2): " << printHelper(p1 > p2) << std::endl;

    // 比較 p1 和 p3
    std::cout << "(p1 == p3): " << printHelper(p1 == p3) << std::endl;
    std::cout << "(p1 < p3): " << printHelper(p1 < p3) << std::endl;
    std::cout << "(p1 > p3): " << printHelper(p1 > p3) << std::endl;

    return 0;
}

使用時機

  1. 需要比較對象:當需要對對象進行排序或比較時,例如在排序演算法、二叉搜索樹、優先隊列等數據結構中。

  2. 需要多個比較運算符:當類需要定義多個比較運算符時(例如 <, <=, >, >=, ==, !=),使用 <=> 可以簡化這些運算符的定義。

  3. 實現自定義排序邏輯:當類需要自定義排序邏輯時,可以通過自定義 <=> 運算符來實現。

以上。呼

☝ツ☝

This post is licensed under CC BY 4.0 by the author.

👈 ツ 👍

Item 29 - Three-way Comparison Operator <=> (English)

Item 8(1/2) - Enhanced Multithreading Support(English)