コンテンツにスキップ

C++/三方比較演算子

出典: フリー教科書『ウィキブックス(Wikibooks)』
< C++

C++20で導入された比較演算子 <=> は、通称「三方比較演算子」や「宇宙船演算子」と呼ばれます。この演算子は、二つの値を比較して、それらの関係(小さい、大きい、等しい)を一度に示すことができる総称的な比較を行います。

基本的な機能[編集]

<=> 演算子は、以下のように二つのオペランドを比較し、その結果を std::strong_orderingstd::weak_ordering、または std::partial_ordering のいずれかとして返します。これにより、単一の演算子で複数の比較結果を扱うことができます。

#include <compare>
#include <iostream>

auto main() -> int {
    int const a{5};
    int const b{10};

    auto result = a <=> b;

    if (result < nullptr) {
        std::cout << "a is less than b" << std::endl;
    } else if (result > nullptr) {
        std::cout << "a is greater than b" << std::endl;
    } else {
        std::cout << "a is equal to b" << std::endl;
    }

    return 0;
}

この例では、a <=> b の結果は std::strong_ordering 型で、これは lessgreater、または equivalent という三つの状態を持ちます。

比較カテゴリ[編集]

<=> 演算子は、三つの比較カテゴリをサポートします:

std::strong_ordering
完全な順序付けを表します。<, <=, >, >=, ==, != を使用でき、通常の比較演算子と互換性があります。
std::weak_ordering
弱い順序付けを表します。等しい要素が存在する場合に等価と見なしますが、完全な順序付けは保証しません。
std::partial_ordering
部分的な順序付けを表します。順序が定義されていない可能性のある値も扱えます。

クラスへの適用[編集]

クラスに <=> 演算子を適用することで、そのクラスのオブジェクトを簡単に比較できるようにすることができます。これには、自動生成される比較演算子を利用する方法と、自分で比較関数を定義する方法があります。

自動生成される比較演算子[編集]

C++20では、<=> 演算子を使用して自動的に比較関数を生成することができます。これは、default キーワードを使用するだけで可能です。

#include <compare>
#include <iostream>

struct Point {
    int x;
    int y;

    auto operator<=>(const Point&) const = default;
};

auto main() -> int {
    Point p1{1, 2};
    Point p2{2, 3};

    if (p1 < p2) {
        std::cout << "p1 is less than p2" << std::endl;
    } else if (p1 > p2) {
        std::cout << "p1 is greater than p2" << std::endl;
    } else {
        std::cout << "p1 is equal to p2" << std::endl;
    }

    return 0;
}

NaN[編集]

<=> 演算子は、通常の比較演算子とは異なり、NaN(非数)を扱う際に注意が必要です。

通常の比較演算子では、NaNはどの値とも等しくないと評価されます。しかし、<=> 演算子では、NaNとの比較結果は std::partial_ordering::unordered となります。つまり、NaN同士やNaNとの他の値との比較は、完全な順序付けができないという意味で「未定義」です。

以下は、NaNを含む比較演算子の動作の例です。

#include <iostream>
#include <compare>
#include <cmath> // NaNを扱うために追加

auto main() -> int {
    double nan1 = std::nan("1");
    double nan2 = std::nan("2");

    // <=> 演算子の結果は std::partial_ordering で、std::unordered を返します
    auto result1 = nan1 <=> nan2;
    if (result1 == std::partial_ordering::unordered) {
        std::cout << "nan1 is unordered with nan2" << std::endl;
    }

    // 比較演算子は NaN との比較結果が false を返します
    if (nan1 == nan2) {
        std::cout << "nan1 is equal to nan2" << std::endl;
    } else {
        std::cout << "nan1 is not equal to nan2" << std::endl; // 出力されます
    }

    // 通常の比較演算子は NaN との比較結果が false を返します
    if (nan1 < nan2) {
        std::cout << "nan1 is less than nan2" << std::endl;
    } else {
        std::cout << "nan1 is not less than nan2" << std::endl; // 出力されます
    }

    return 0;
}

このように、<=> 演算子を使用する際には、NaNが関与する可能性に注意する必要があります。NaNとの比較は常に std::partial_ordering::unordered を返すため、正確な比較が必要な場合には、別途 NaN のチェックが必要です。

カスタム比較関数[編集]

自動生成される比較演算子が適用できない場合や、独自の比較ロジックが必要な場合には、自分で比較関数を定義することができます。

#include <compare>
#include <iostream>

struct Point {
    int x;
    int y;

    std::partial_ordering operator<=>(const Point& other) const {
        if (auto cmp = x <=> other.x; cmp != 0) return cmp;
        return y <=> other.y;
    }
};

auto main() -> int {
    Point p1{1, 2};
    Point p2{1, 3};

    if (p1 < p2) {
        std::cout << "p1 is less than p2" << std::endl;
    } else if (p1 > p2) {
        std::cout << "p1 is greater than p2" << std::endl;
    } else {
        std::cout << "p1 is equal to p2" << std::endl;
    }

    return 0;
}

利便性[編集]

<=> 演算子を使用することで、以下のような利点があります:

簡潔なコード
単一の演算子で複数の比較結果を扱えるため、コードが簡潔になります。
標準化された比較
標準ライブラリの型と一貫性のある比較結果を得ることができます。
自動生成
クラスのメンバ変数に基づいた比較関数を自動生成することで、手動での定義を減らすことができます。

まとめ[編集]

C++20で導入された比較演算子 <=> は、プログラムの比較処理を簡素化し、コードの可読性と保守性を向上させる強力な機能です。自動生成される比較関数やカスタム比較関数を使用することで、効率的な比較ロジックを実装することができます。