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, Catはthree_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であることCatがstrong_orderingに変換可能ならばTはtotally_orderedのモデルである
-
(2) :
const remove_reference_t<T>, const remove_reference_t<U>の左辺値t, u、C = common_reference_t<const remove_reference_t<T>&, const remove_reference_t<U>&>について次の条件を満たす場合に限って、型T, U, Catはthree_way_comparable_withのモデルであるt <=> uとu <=> 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であることCatがstrong_orderingに変換可能ならばT, Uはtotally_ordered_withのモデルである
-
partially-ordered-with:const remove_reference_t<T>, const remove_reference_t<U>の左辺値t, uについて次の条件を満たす場合に限って、型T, U, Catはpartially-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
処理系
- Clang: ??
- GCC: 10.1 ✅
- Visual C++: ??