Namespaces
Variants

Default comparisons (since C++20)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

比較演算子関数は明示的にデフォルト設定でき、クラスに対して対応するデフォルト比較をコンパイラに生成するよう要求できます。

目次

定義

デフォルト化比較演算子関数とは、以下のすべての条件を満たす非テンプレート比較演算子関数(すなわち <=> == != < > <= 、または >= )のことです:

このような比較演算子関数は、 クラス C に対するデフォルト比較演算子関数 と呼ばれます。

struct X
{
    bool operator==(const X&) const = default; // OK
    bool operator==(const X&) = default;       // エラー: 暗黙のオブジェクト
                                               //        パラメータ型は X&
    bool operator==(this X, X) = default;      // OK
};
struct Y
{
    friend bool operator==(Y, Y) = default;        // OK
    friend bool operator==(Y, const Y&) = default; // エラー: 異なるパラメータ型
};
bool operator==(const Y&, const Y&) = default;     // エラー: Yのフレンドではない

比較演算子関数の暗黙的な定義における名前探索とアクセスチェックは、その関数本体と同等のコンテキストから実行されます。クラス内でデフォルト化された比較演算子関数の定義は、その関数の最初の宣言でなければなりません。

デフォルト比較順序

クラス C が与えられたとき、サブオブジェクトリストは以下のサブオブジェクトによって順に形成されます:

  • C の直接基底クラスの部分オブジェクト(宣言順)
  • C の非静的 data members (宣言順)
  • いずれかのメンバー部分オブジェクトが配列型である場合、添字の昇順でその要素のシーケンスに展開されます。この展開は再帰的です:配列型の配列要素は、配列型の部分オブジェクトがなくなるまで再度展開されます。

C の任意のオブジェクト x について、以下の説明において:

struct S {};
struct T : S
{
    int arr[2][2];
} t;
// 「t」の部分オブジェクトリストは、以下の5つの部分オブジェクトを順に含む:
// (S)t → t[0][0] → t[0][1] → t[1][0] → t[1][1]

三方比較

クラス型の operator <=> は、任意の戻り値型でデフォルト定義することができます。

比較カテゴリ型

比較カテゴリ型は3種類あります:

等価な値は.. 比較不能な値は..
std::strong_ordering 区別できない 許可されない
std::weak_ordering 区別できる 許可されない
std::partial_ordering 区別できる 許可される

合成三項比較

合成された三方比較 は、同じ型の T 型の左辺値 a b の間で以下のように定義されます:

  • a <=> b のオーバーロード解決が使用可能な候補を生成し、 static_cast を用いて明示的に T へ変換可能な場合、合成された比較は static_cast<T>(a <=> b) となる。
  • それ以外の場合、以下の条件のいずれかが満たされる場合、合成された比較は定義されない:
  • a <=> b のオーバーロード解決が少なくとも1つの有効な候補を見つける。
  • T が比較カテゴリ型ではない。
  • a == b のオーバーロード解決が使用可能な候補を結果として生成しない。
  • a < b のオーバーロード解決が使用可能な候補を結果として生成しない。
a == b ? std::strong_ordering::equal :
a < b  ? std::strong_ordering::less :
         std::strong_ordering::greater
a == b ? std::weak_ordering::equivalent :
a < b  ? std::weak_ordering::less :
         std::weak_ordering::greater
a == b ? std::partial_ordering::equivalent :
a < b  ? std::partial_ordering::less :
b < a  ? std::partial_ordering::greater : 
         std::partial_ordering::unordered

プレースホルダー戻り値型

クラス型 C のデフォルト化された三方比較演算子関数( operator <=> )の宣言された戻り値の型が auto である場合、戻り値の型は型 C のオブジェクト x の対応する部分オブジェクト間の三方比較の戻り値の型から推論されます。

x (展開された)サブオブジェクトリスト 内の各サブオブジェクト x_i について:

  1. x_i <=> x_i に対してオーバーロード解決を実行し、オーバーロード解決が有効な候補を結果として得られない場合、デフォルト化された operator <=> は削除済みとして定義されます。
  2. x_i <=> x_i の型のCV修飾なしバージョンを R_i と表記します。 R_i が比較カテゴリ型でない場合、デフォルト化された operator <=> は削除済みとして定義されます。

デフォルト化された operator <=> が削除定義されていない場合、その戻り値型は std:: common_comparison_category_t < R_1, R_2, ..., R_n > と推論されます。

非プレースホルダー戻り値型

デフォルト化された operator <=> の宣言された戻り値の型が auto でない場合、いかなる プレースホルダ型 も含むことはできません(例: decltype ( auto ) )。

(展開された)サブオブジェクトリスト内に x_i が存在し、宣言された戻り値型の 合成三方向比較 x_i x_i の間で定義されていない場合、デフォルト化された operator <=> は削除済みとして定義されます。

比較結果

x y をデフォルト化された operator <=> のパラメータとし、 x y の(展開された)サブオブジェクトリスト内の各サブオブジェクトをそれぞれ x_i y_i と表記する。 x y の間のデフォルトの三方比較は、対応するサブオブジェクト x_i y_i i の昇順で比較することによって実行される。

R を(推論される可能性のある)戻り値の型とし、 x_i y_i の間の比較結果は、型 R の合成された三方比較の結果である。

  • デフォルトの三方比較において x y の比較を行う際、 x_i y_i の部分オブジェクト比較で結果 v_i が生成され、 v_i ! = 0 bool へ文脈的に変換した結果が true となる場合、戻り値は v_i のコピーとなる(残りの部分オブジェクトは比較されない)。
  • それ以外の場合、戻り値は static_cast < R > ( std :: strong_ordering :: equal ) となる。
#include <compare>
#include <iostream>
#include <set>
struct Point
{
    int x;
    int y;
    auto operator<=>(const Point&) const = default;
    /* 比較以外の関数 */
};
int main()
{
    Point pt1{1, 1}, pt2{1, 2};
    std::set<Point> s; // OK
    s.insert(pt1);     // OK
    // 双方向比較演算子関数を明示的に定義する必要はありません:
    // operator== は暗黙的に宣言されます(下記参照)
    // 他の候補のオーバーロード解決では書き換えられた候補が選択されます
    std::cout << std::boolalpha
        << (pt1 == pt2) << ' '  // false
        << (pt1 != pt2) << ' '  // true
        << (pt1 <  pt2) << ' '  // true
        << (pt1 <= pt2) << ' '  // true
        << (pt1 >  pt2) << ' '  // false
        << (pt1 >= pt2) << ' '; // false
}

等価比較

明示的宣言

クラス型の operator == は、 bool 型の戻り値を持つデフォルト定義として定義できます。

クラス C と型 C のオブジェクト x が与えられたとき、 x の(展開された)部分オブジェクトリスト内の部分オブジェクト x_i について、 x_i == x_i のオーバーロード解決が使用可能な候補を生成しない場合、デフォルト化された operator == は削除済みとして定義されます。

x y をデフォルト化された operator == のパラメータとし、 x y の(展開された)サブオブジェクトリスト内の各サブオブジェクトをそれぞれ x_i y_i と表記します。 x y の間のデフォルト等価比較は、対応するサブオブジェクト x_i y_i i の昇順で比較することによって実行されます。

x_i y_i の比較結果は、 x_i == y_i の結果です。

  • デフォルトの等価比較において x y の間で部分オブジェクトごとの比較を行った際、 x_i y_i の比較結果 v_i を文脈上 bool に変換した結果が false となる場合、戻り値は false となる(残りの部分オブジェクトは比較されない)。
  • それ以外の場合、戻り値は true となる。
#include <iostream>
struct Point
{
    int x;
    int y;
    bool operator==(const Point&) const = default;
    /* 比較以外の関数 */
};
int main()
{
    Point pt1{3, 5}, pt2{2, 5};
    std::cout << std::boolalpha
        << (pt1 != pt2) << '\n'  // true
        << (pt1 == pt1) << '\n'; // true
    struct [[maybe_unused]] { int x{}, y{}; } p, q;
    // if (p == q) {} // エラー: operator== が定義されていません
}

暗黙の宣言

クラス C が明示的に operator == という名前のメンバーやフレンドを宣言していない場合、デフォルトとして定義された各 operator <=> に対して、暗黙的に == 演算子関数が宣言されます。各暗黙的に宣言された operator == は、対応するデフォルトの operator <=> と同じアクセス権、 関数定義 、および同じ クラススコープ を持ち、以下の変更が加えられます:

  • 宣言子識別子は operator == に置き換えられます。
  • 戻り値の型は bool に置き換えられます。
template<typename T>
struct X
{
    friend constexpr std::partial_ordering operator<=>(X, X)
        requires (sizeof(T) != 1) = default;
    // 暗黙的に宣言: friend constexpr bool operator==(X, X)
    //                          requires (sizeof(T) != 1) = default;
    [[nodiscard]] virtual std::strong_ordering operator<=>(const X&) const = default;
    // 暗黙的に宣言: [[nodiscard]] virtual bool
    //                          operator==(const X&) const = default;
};

セカンダリ比較

クラス型に対する二次比較演算子関数( != < > <= または >= )は、戻り値型 bool でデフォルト定義することができます。

@ を5つの二次比較演算子の1つとし、各デフォルト化された operator@ について、パラメータ x y を持つ場合、最大2回のオーバーロード解決が実行され(デフォルト化された operator@ を候補として考慮しない)、これが削除定義されているかどうかが判定される。

  • 最初のオーバーロード解決は x @ y に対して実行される。オーバーロード解決が使用可能な候補を結果として生成しない場合、または選択された候補が 書き換え候補 でない場合、デフォルト化された operator@ は削除済みとして定義される。これらのケースでは2回目のオーバーロード解決は行われない。
  • 2回目のオーバーロード解決は、選択された書き換え候補に対して x @ y に対して実行される。オーバーロード解決が使用可能な候補を結果として生成しない場合、デフォルト化された operator@ は削除済みとして定義される。

x @ y bool に暗黙的に変換できない場合、デフォルト化された operator@ は削除定義されます。

デフォルト化された operator@ が削除定義されていない場合、それは x @ y を生成します。

struct HasNoRelational {};
struct C
{
    friend HasNoRelational operator<=>(const C&, const C&);
    bool operator<(const C&) const = default; // OK、関数はデフォルト化されている
};

キーワード

default

不具合報告

以下の動作変更の欠陥報告書は、以前に公開されたC++規格に対して遡及的に適用されました。

DR 適用対象 公開時の動作 正しい動作
CWG 2539 C++20 合成された三方比較は、明示的変換が利用できない場合でも
static_cast を選択していた
この場合
static_cast を選択しない
CWG 2546 C++20 デフォルト化された二次的な operator@ は、
x @ y のオーバーロード解決が使用不可能な
書き換え候補を選択した場合に削除定義されていなかった
この場合
削除定義される
CWG 2547 C++20 非クラスに対する比較演算子関数を
デフォルト化できるかどうかが不明確だった
デフォルト化できない
CWG 2568 C++20 比較演算子関数の暗黙的な定義が
メンバーアクセス規則に違反する可能性があった
アクセスチェックは
それらの関数本体と同等の
コンテキストから実行される

関連項目