最終更新日時(UTC):
が更新

履歴 編集

concept
<compare>

std::three_way_comparable(C++20)

namespace std {
  template<class T, class Cat = partial_ordering>
  concept three_way_comparable = /*see below*/;             // (1)

  template<class T, class U, class Cat = partial_ordering>
  concept three_way_comparable_with = /*see below*/;        // (2)
}

概要

three_way_comparable及びthree_way_comparable_withは、指定された型TもしくはT, Uの間で<=>による三方比較を使用可能であり、その戻り値型が指定した比較カテゴリ型Catに変換可能であることを表すコンセプトである。

要件

まず、説明専用のコンセプトcompares-as及びpartially-ordered-withを以下のように定義する。

//Catがvoidでないなら、TとCatは比較カテゴリ型でありTはCatへ変換可能である
template<class T, class Cat>
concept compares-as = same_as<common_comparison_category_t<T, Cat>, Cat>;

//順序付けの4種×2方向の比較演算子が使用可能であり、戻り値型がboolean-testableコンセプトを満たす
template<class T, class U>
concept partially-ordered-with =
  requires(const remove_reference_t<T>& t, const remove_reference_t<U>& u) {
    { t <  u } -> boolean-testable;
    { t >  u } -> boolean-testable;
    { t <= u } -> boolean-testable;
    { t >= u } -> boolean-testable;
    { u <  t } -> boolean-testable;
    { u >  t } -> boolean-testable;
    { u <= t } -> boolean-testable;
    { u >= t } -> boolean-testable;
  };

  • (1) : 以下のように定義される。

template<class T, class Cat = partial_ordering>
concept three_way_comparable =
  weakly-equality-comparable-with<T, T> &&
  partially-ordered-with<T, T> &&
  requires(const remove_reference_t<T>& a, const remove_reference_t<T>& b) {
    { a <=> b } -> compares-as<Cat>;
  };

  • (2) : 以下のように定義される。

template<class T, class U, class Cat = partial_ordering>
concept three_way_comparable_with =
  three_way_comparable<T, Cat> &&
  three_way_comparable<U, Cat> &&
  common_reference_with<const remove_reference_t<T>&, const remove_reference_t<U>&> &&
  three_way_comparable<
    common_reference_t<const remove_reference_t<T>&, const remove_reference_t<U>&>, Cat> &&
  weakly-equality-comparable-with<T, U> &&
  partially-ordered-with<T, U> &&
  requires(const remove_reference_t<T>& t, const remove_reference_t<U>& u) {
    { t <=> u } -> compares-as<Cat>;
    { u <=> t } -> compares-as<Cat>;
  };

モデル

  • (1) : const remove_reference_t<T>の左辺値a, bについて次の条件を満たす場合に限って、型T, Catthree_way_comparableのモデルである

    • (a <=> b == 0) == bool(a == b)trueであること
    • (a <=> b != 0) == bool(a != b)trueであること
    • ((a <=> b) <=> 0)(0 <=> (a <=> b))が等値
    • (a <=> b < 0) == bool(a < b)trueであること
    • (a <=> b > 0) == bool(a > b)trueであること
    • (a <=> b <= 0) == bool(a <= b)trueであること
    • (a <=> b >= 0) == bool(a >= b)trueであること
    • Catstrong_orderingに変換可能ならば
      • Ttotally_orderedのモデルである
  • (2) : const remove_reference_t<T>, const remove_reference_t<U>の左辺値t, uC = common_reference_t<const remove_reference_t<T>&, const remove_reference_t<U>&>について次の条件を満たす場合に限って、型T, U, Catthree_way_comparable_withのモデルである

    • t <=> uu <=> tが同じ定義域を持つ
    • ((t <=> u) <=> 0)(0 <=> (t <=> u))が等値
    • (t <=> u == 0) == bool(t == u)trueであること
    • (t <=> u != 0) == bool(t != u)trueであること
    • Cat(t <=> u) == Cat(C(t) <=> C(u))trueであること
    • (t <=> u < 0) == bool(t < u)trueであること
    • (t <=> u > 0) == bool(t > u)trueであること
    • (t <=> u <= 0) == bool(t <= u)trueであること
    • (t <=> u >= 0) == bool(t >= u)trueであること
    • Catstrong_orderingに変換可能ならば
      • T, Utotally_ordered_withのモデルである
  • partially-ordered-with : const remove_reference_t<T>, const remove_reference_t<U>の左辺値t, uについて次の条件を満たす場合に限って、型T, U, Catpartially-ordered-withのモデルである

    • t < u, t <= u, t > u, t >= u, u < t, u <= t, u > t, u >= tが全て同じ定義域を持つ
    • bool(t < u) == bool(u > t)trueであること
    • bool(u < t) == bool(t > u)trueであること
    • bool(t <= u) == bool(u >= t)trueであること
    • bool(u <= t) == bool(t >= u)trueであること

three_way_comparable

#include <iostream>
#include <compare>

//<=>が使用可能ならそれを使用して比較結果を出力
template<std::three_way_comparable T>
void print_is_less(const T& t, const T& u) {
  std::cout << "<=> : " << ((t <=> u) < 0) << std::endl;
}

//<=>が使用可能でないなら<演算子を使用
template<typename T>
void print_is_less(const T& t, const T& u) {
  std::cout << "<   : " << (t < u) << std::endl;
}


//<演算子だけが使用可能
struct L {
  int n;
  friend bool operator<(const L& a, const L& b) { return a.n < b.n;}
};

//<=>演算子含め、全ての比較演算が可能
struct S {
  int n;

  friend auto operator<=>(const S& a, const S& b) = default;
  //friend bool operator== (const S& a, const S& b) = default;
};


int main() {
  std::cout << std::boolalpha;
  L l1{1}, l2{2};
  S s1{1}, s2{2};

  print_is_less(1, 2);
  print_is_less(-0.0, +0.0);
  print_is_less(l1, l2);
  print_is_less(s1, s2);
}

出力

<=> : true
<=> : false
<   : true
<=> : true

three_way_comparable_with

#include <iostream>
#include <compare>

//<=>が使用可能ならそれを使用して比較結果を出力
template<typename T, typename U>
requires std::three_way_comparable_with<T, U>
void print_is_less(const T& t, const U& u) {
  std::cout << "<=> : " << ((t <=> u) < 0) << std::endl;
}

//<=>が使用可能でないなら<演算子を使用
template<typename T, typename U>
void print_is_less(const T& t, const U& u) {
  std::cout << "<   : " << (t < u) << std::endl;
}

//共通の参照型を作るために必要
struct B {
  friend auto operator<=>(const B&, const B&) = default;
  //friend bool operator== (const B&, const B&) = default;
};

struct S1 : B {
  std::size_t s;

  friend auto operator<=>(const S1&, const S1&) = default;
  //friend bool operator== (const S1&, const S1&) = default;
};

struct S2 : B {
  std::size_t s;

  friend auto operator<=>(const S2&, const S2&) = default;
  friend bool operator== (const S2&, const S2&) = default;  //この宣言は必要

  friend bool operator== (const S1& a, const S2& b) { return a.s ==  b.s;}
  friend auto operator<=>(const S1& a, const S2& b) { return a.s <=> b.s;}
};

//std::common_referenceおよびstd::common_reference_withにアダプトする
namespace std {
  template<template<class> class TQual, template<class> class UQual>
  struct basic_common_reference<S1, S2, TQual, UQual> {
    using type = const B&;  //const必須
  };

  //対称性のために引数順を逆にしたものも定義する
  template<template<class> class TQual, template<class> class UQual>
  struct basic_common_reference<S2, S1, TQual, UQual> {
    using type = const B&;  //const必須
  };
}


int main() {
  std::cout << std::boolalpha;
  S1 s1{{}, 0u};
  S2 s2{{}, 2u};

  print_is_less(s1, s2);
  print_is_less(s2, s1);
}

出力

<=> : true
<=> : false

バージョン

言語

  • C++20

処理系

関連項目

参照