名前空間
変種
操作

デフォルト比較 (C++20 以降)

From cppreference.com
< cpp‎ | language
 
 
C++言語
全般
フロー制御
条件実行文
if
繰り返し文 (ループ)
for
範囲for (C++11)
ジャンプ文
関数
関数宣言
ラムダ式
inline指定子
動的例外仕様 (C++17まで*)
noexcept指定子 (C++11)
例外
名前空間
指定子
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点数
文字 - 文字列 - nullptr (C++11)
ユーザー定義 (C++11)
ユーティリティ
属性 (C++11)
typedef宣言
型エイリアス宣言 (C++11)
キャスト
メモリ確保
クラス
クラス固有の関数プロパティ
explicit (C++11)
static

特殊メンバ関数
テンプレート
その他
 
 

比較演算子関数を明示的にデフォルト指定することで、クラスに対応するデフォルト比較をコンパイラに生成させることができます。

目次

[編集] 定義

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

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

struct X
{
    bool operator==(const X&) const = default; // OK
    bool operator==(const X&) = default;       // Error: the implicit object
                                               //        parameter type is X&
    bool operator==(this X, X) = default;      // OK
};
 
struct Y
{
    friend bool operator==(Y, Y) = default;        // OK
    friend bool operator==(Y, const Y&) = default; // Error: different parameter types
};
 
bool operator==(const Y&, const Y&) = default;     // Error: not a friend of Y

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

[編集] デフォルト比較順序

クラス C が与えられた場合、以下の要素を順に並べたサブオブジェクトリストが形成されます。

  • C の直接基底クラスのサブオブジェクトを、宣言順に。
  • C の非静的データメンバを、宣言順に。
  • いずれかのメンバサブオブジェクトが配列型の場合、その要素を添え字の昇順で展開します。展開は再帰的です。配列型の配列要素は、サブオブジェクトが配列型でなくなるまで再度展開されます。

あらゆる C 型のオブジェクト x について、以下の説明で

struct S {};
 
struct T : S
{
    int arr[2][2];
} t;
 
// The subobject list for “t” consists of the following 5 subobjects in order:
// (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  識別可能 許可

[編集] 合成三方向比較

同じ型のglvalue ab の間の型 T合成三方向比較は、次のように定義されます。

  • a <=> b のオーバーロード解決が使用可能な候補をもたらし、それを Tstatic_cast で明示的に変換できる場合、合成比較は static_cast<T>(a <=> b) となります。
  • それ以外の場合で、以下のいずれかの条件が満たされる場合、合成比較は未定義となります。
  • a <=> b のオーバーロード解決が、少なくとも1つの実行可能候補を見つける。
  • T が比較カテゴリ型ではない。
  • a == b のオーバーロード解決が、使用可能な候補をもたらさない。
  • a < b のオーバーロード解決が、使用可能な候補をもたらさない。
  • それ以外の場合で、Tstd::strong_ordering の場合、合成比較は次のようになります。
a == b ? std::strong_ordering::equal :
a < b  ? std::strong_ordering::less :
         std::strong_ordering::greater
  • それ以外の場合で、Tstd::weak_ordering の場合、合成比較は次のようになります。
a == b ? std::weak_ordering::equivalent :
a < b  ? std::weak_ordering::less :
         std::weak_ordering::greater
  • それ以外の場合(Tstd::partial_ordering の場合)、合成比較は次のようになります。
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 の(展開された)サブオブジェクトリストに、宣言された戻り値型の x_ix_i 間の合成三方向比較が未定義となるサブオブジェクト x_i が存在する場合、デフォルト指定された operator<=> は削除として定義されます。

[編集] 比較結果

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

R を(推導された、または宣言された)戻り値型とします。x_iy_i の間の比較結果は、x_iy_i の間の型 R における合成三方向比較の結果となります。

  • xy の間のデフォルト三方向比較中に、x_iy_i の間のサブオブジェクトごとの比較が結果 v_i を生成し、v_i != 0bool に文脈変換すると 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;
    /* non-comparison functions */
};
 
int main()
{
    Point pt1{1, 1}, pt2{1, 2};
    std::set<Point> s; // OK
    s.insert(pt1);     // OK
 
    // two-way comparison operator functions are not required to be explicitly defined:
    // operator== is implicitly declared (see below)
    // the overload resolutions of other candidates will select rewritten candidates 
    std::cout << std::boolalpha
        << (pt1 == pt2) << ' '  // false
        << (pt1 != pt2) << ' '  // true
        << (pt1 <  pt2) << ' '  // true
        << (pt1 <= pt2) << ' '  // true
        << (pt1 >  pt2) << ' '  // false
        << (pt1 >= pt2) << ' '; // false
}

[編集] 等価比較

[編集] 明示的宣言

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

クラス CC 型のオブジェクト x が与えられた場合、x の(展開された)サブオブジェクトリストに、x_i == x_i のオーバーロード解決が使用可能な候補をもたらさないサブオブジェクト x_i が存在する場合、デフォルト指定された operator== は削除として定義されます。

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

x_iy_i の間の比較結果は、x_i == y_i の結果となります。

  • xy の間のデフォルト等価比較中に、x_iy_i の間のサブオブジェクトごとの比較が結果 v_i を生成し、v_ibool に文脈変換すると false になる場合、戻り値は false となります(残りのサブオブジェクトは比較されません)。
  • それ以外の場合、戻り値は true となります。
#include <iostream>
 
struct Point
{
    int x;
    int y;
    bool operator==(const Point&) const = default;
    /* non-comparison functions */
};
 
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) {} // Error: operator== is not defined
}

[編集] 暗黙的宣言

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

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

[編集] 二次比較

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

@ を5つの二次比較演算子のいずれかとし、パラメータ x および y を持つデフォルト指定された operator@ ごとに、それが削除として定義されるかどうかを判断するために、最大2回のオーバーロード解決が実行されます(デフォルト指定された operator@ は候補として考慮されません)。

  • 最初のオーバーロード解決は x @ y に対して実行されます。オーバーロード解決が使用可能な候補をもたらさない場合、または選択された候補が書き換えられた候補でない場合、デフォルト指定された operator@ は削除として定義されます。この場合、2回目のオーバーロード解決はありません。
  • 2回目のオーバーロード解決は、選択された x @ y の書き換えられた候補に対して実行されます。オーバーロード解決が使用可能な候補をもたらさない場合、デフォルト指定された operator@ は削除として定義されます。

x @ ybool に暗黙変換できない場合、デフォルト指定された operator@ は削除として定義されます。

デフォルト指定された operator@ が削除として定義されていない場合、x @ y の値を返します。

struct HasNoRelational {};
 
struct C
{
    friend HasNoRelational operator<=>(const C&, const C&);
    bool operator<(const C&) const = default; // OK, function is defaulted
};

[編集] キーワード

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 比較演算子関数の暗黙の定義が
メンバアクセス規則に違反する可能性がある
アクセスチェックは、
関数本体と同等の
コンテキストから実行される

[編集] 関連項目

English 日本語 中文(简体) 中文(繁體)