C++20:等しい概念と順序付けを定義する

前回の投稿で、私は概念「平等」を定義しました。ここで、さらに一歩進んで、Equal という概念を使用して Ordering という概念を定義します。

ここで、前回の投稿の最後を簡単に思い出してください。 Equal の概念とそれを使用する関数 areEqual を定義しました。

template<typename T>
concept Equal =
 requires(T a, T b) {
 { a == b } -> bool;
 { a != b } -> bool;
};


bool areEqual(Equal auto fir, Equal auto sec) { 
 return fir == sec;
}

Equal という概念の間違った使い方

前回の投稿で、私は平等の概念を間違った方法で使用しました。 Equal という概念では、a と b が同じ型である必要がありますが、関数 areEqual では、fir と sec が異なる型であり、両方が Equal という概念をサポートしている可能性があります。プレースホルダー構文の代わりに制約付きテンプレート パラメーターを使用すると、問題が解決します:

template <Equal T>
bool areEqual(T fir, T sec) {
 fir == sec;
}

ここで、fir と sec は同じ型でなければなりません。

この矛盾を指摘してくれた Corentin Jabot に感謝します。

さらに、概念 Equal は、等号および不等号演算子が bool を返すかどうかをチェックするべきではありませんが、暗黙的または明示的に bool に変換できるものを返します。ここにいます。

template<typename T>
concept Equal =
 requires(T a, T b) {
 { a == b } -> std::convertible_to<bool>;
 { a != b } -> std::convertible_to<bool>;
};

追加する必要があります。 std::convertible_to は概念であるため、ヘッダー が必要です。

template <class From, class To>
concept convertible_to =
 std::is_convertible_v<From, To> &&
 requires(From (&f)()) {
 static_cast<To>(f());
 };

C++ 20 標準では、等値比較の 2 つの概念が既に定義されています。

  • std::equality_comparabl e:Equal という私の概念に対応
  • std::equality_comparable_with :異なる型の値を比較できます。例:1.0 ==1.0f

チャレンジ

Haskell の型クラス階層の一部を提示して、前回の投稿を終了しました。

クラス階層は、型クラス Ord が型クラス Eq の改良版であることを示しています。これは、Haskell でエレガントに表現できます。

class Eq a where
 (==) :: a -> a -> Bool
 (/=) :: a -> a -> Bool

class Eq a => Ord a where
 compare :: a -> a -> Ordering
 (<) :: a -> a -> Bool
 (<=) :: a -> a -> Bool
 (>) :: a -> a -> Bool
 (>=) :: a -> a -> Bool
 max :: a -> a -> a

これが私の課題です。このような関係を C++20 の概念で非常にエレガントに表現できますか?簡単にするために、Haskell の型クラスの関数 compare と max は無視します。もちろんできます。

コンセプトの順序付け

requires-expression のおかげで、Ordering という概念の定義は、型クラス Equal の定義と非常によく似ています。

template <typename T>
concept Ordering =
 Equal<T> &&
 requires(T a, T b) {
 { a <= b } -> std::convertible_to<bool>;
 { a < b } -> std::convertible_to<bool>;
 { a > b } -> std::convertible_to<bool>;
 { a >= b } -> std::convertible_to<bool>;
 };

よし、やってみよう。

// conceptsDefinitionOrdering.cpp

#include <concepts>
#include <iostream>
#include <unordered_set>

template<typename T>
concept Equal =
 requires(T a, T b) {
 { a == b } -> std::convertible_to<bool>;
 { a != b } -> std::convertible_to<bool>;
 };


template <typename T>
concept Ordering =
 Equal<T> &&
 requires(T a, T b) {
 { a <= b } -> std::convertible_to<bool>;
 { a < b } -> std::convertible_to<bool>;
 { a > b } -> std::convertible_to<bool>;
 { a >= b } -> std::convertible_to<bool>;
 };

template <Equal T>
bool areEqual(T a, T b) {
 return a == b;
}

template <Ordering T>
T getSmaller(T a, T b) {
 return (a < b) ? a : b;
}
 
int main() {
 
 std::cout << std::boolalpha << std::endl;
 
 std::cout << "areEqual(1, 5): " << areEqual(1, 5) << std::endl;
 
 std::cout << "getSmaller(1, 5): " << getSmaller(1, 5) << std::endl;
 
 std::unordered_set<int> firSet{1, 2, 3, 4, 5};
 std::unordered_set<int> secSet{5, 4, 3, 2, 1};
 
 std::cout << "areEqual(firSet, secSet): " << areEqual(firSet, secSet) << std::endl;
 
 // auto smallerSet = getSmaller(firSet, secSet);
 
 std::cout << std::endl;
 
}

関数 getSmaller では、引数 a と b の両方が順序付けの概念をサポートし、両方が同じ型を持っている必要があります。この要件は、数字の 1 と 5 にも当てはまります。

もちろん、std::unordered_set は順序付けをサポートしていません。実際の msvc コンパイラは、/std:c++latest フラグを指定して auto small =getSmaller(firSet, secSet) 行をコンパイルしようとすると、非常に特殊です。

ところで。エラー メッセージは非常に明確です:関連する制約が満たされていません。

もちろん、順序付けの概念は既に C++20 標準の一部です。

  • std::three_way_comparable: 私のコンセプトに対応する注文
  • std::three_way_comparable_with: 異なる型の値を比較できます。例:1.0 <1.0f

たぶん、あなたは三者択一という言葉にいらいらしているでしょう。 C++20 では、宇宙船演算子とも呼ばれる 3 者間比較演算子を取得します。 <=>。最初の概要は次のとおりです:C++20:コア言語。 3 者間比較演算子については、今後の投稿で書きます。

コンパイラ サポート

試してみることで新しいことを学びます。おそらく、実際の msvc が利用可能ではないのでしょう。この場合、コンパイラ エクスプローラで現在の GCC (トランク) を使用します。 GCC は、概念の C++20 構文をサポートしています。さらなる実験のための conceptDefinitionOrdering.cpp は次のとおりです:https://godbolt.org/z/uyVFX8.

次は?

C++ エコシステムで適切に機能する具象型を定義する場合は、「int へのリンクとして動作する」型を定義する必要があります。このような具象型はコピーでき、コピー操作の結果は元のものとは独立しており、同じ値になります。正式には、具象型は通常の型である必要があります。次の投稿では、レギュラーとセミレギュラーの概念を定義します。