C++20:Regular と SemiRegular の概念を定義する

C++ エコシステムで適切に機能する具象型を定義する場合は、「int へのリンクとして動作する」型を定義する必要があります。正式には、具象型は通常の型である必要があります。この投稿では、レギュラーとセミレギュラーの概念を定義します。

C++ では、Regular と SemiRegular は重要な概念です。申し訳ありませんが、私は概念を言わなければなりません。たとえば、C++ コア ガイドラインのルール T.46 は次のとおりです。 T.46:テンプレート引数を少なくとも Regular または SemiRegular にする必要があります。ここで、答える必要がある重要な質問が 1 つだけ残っています。Regular または SemiRegular 型とは何ですか?詳細に入る前に、これは非公式の回答です:

  • 通常の型は「リンクは int として動作します」。コピーすることができ、コピー操作の結果は元のものとは独立しており、同じ値になります。

では、もっとフォーマルにしましょう。レギュラー型もセミレギュラー型です。結果として、SemiRegular タイプで開始します。

セミレギュラー

SemiRegular 型は、6 の規則をサポートする必要があり、スワップ可能でなければなりません。

  • デフォルトのコンストラクタ:X()
  • コピー コンストラクタ:X(const X&)
  • 代入のコピー:operator=(const X&)
  • 移動コンストラクタ:X(X&&)
  • 移動代入:operator=(X&&)
  • デストラクタ:~X()
  • スワップ可能:swap(X&, Y&)

これは簡単でした。型特性ライブラリのおかげで、対応する概念を簡単に定義できます。最初に対応する型特性 isSemiRegular を定義し、それを使用して概念 SemiRegular を定義します。

template<typename T>
struct isSemiRegular: std::integral_constant<bool,
 std::is_default_constructible<T>::value &&
 std::is_copy_constructible<T>::value &&
 std::is_copy_assignable<T>::value &&
 std::is_move_constructible<T>::value &&
 std::is_move_assignable<T>::value &&
 std::is_destructible<T>::value &&
 std::is_swappable<T>::value >{};


template<typename T>
concept SemiRegular = isSemiRegular<T>::value;

続けましょう。

レギュラー

たった 1 つの小さなステップで、Regular のコンセプトが完成しました。さらに、SemiRegular の概念に対して、Regular の概念では、型が同等である必要があります。前回の投稿で、「等しい」という概念をすでに定義しました。

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

Equal という概念を再利用して、Regular という概念を定義しましょう。

template<typename T>
concept Regular = Equal<T> && 
SemiRegular<T>;

今、私は興味があります。 C++20 では、SemiRegular と Regular はどのように定義されていますか?

C++20 における規則的および半規則的な概念

template<class T>
concept movable = is_object_v<T> && move_constructible<T> &&
assignable_from<T&, T> && swappable<T>;

template<class T>
concept copyable = copy_constructible<T> && movable<T> && assignable_from<T&, const T&>;

template<class T>
concept semiregular = copyable<T> && default_constructible<T>;

template<class T>
concept regular = semiregular<T> && equality_comparable<T>;

おわかりのように、レギュラーとセミレギュラーの概念を定義する理由はありませんが、それを説明する必要があります.

興味深いことに、レギュラーという概念は私のレギュラーという概念に似ていますが、セミレギュラーという概念は、コピー可能や移動可能などのより基本的な概念で構成されています。移動可能な概念は、type-traits ライブラリの関数 is_object に基づいています。すでに参照されているページから、型特性 is_object の可能な実装を次に示します。

template< class T>
struct is_object : std::integral_constant<bool,
 std::is_scalar<T>::value ||
 std::is_array<T>::value ||
 std::is_union<T>::value ||
 std::is_class<T>::value> {};

私の投稿の最後のステップがありません。試してみましょう。

概念の使用法 通常と通常

簡単にするために、関数テンプレートは、引数が「int のように動作する」かどうかを確認します。これは、私のコンセプト Regular と C++20 のコンセプト Regular を使用して要件を確立することを意味します。

// regularSemiRegular.cpp

#include <concepts>
#include <vector>
#include <utility>

template<typename T>
struct isSemiRegular: std::integral_constant<bool,
 std::is_default_constructible<T>::value &&
 std::is_copy_constructible<T>::value &&
 std::is_copy_assignable<T>::value &&
 std::is_move_constructible<T>::value &&
 std::is_move_assignable<T>::value &&
 std::is_destructible<T>::value &&
 std::is_swappable<T>::value >{};


template<typename T>
concept SemiRegular = isSemiRegular<T>::value;

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> // (1)
concept Regular = Equal<T> && 
 SemiRegular<T>;

template <Regular T> // (2)
void behavesLikeAnInt(T) {
 // ...
}

template <std::regular T> // (3)
void behavesLikeAnInt2(T) {
 // ...
}

struct EqualityComparable { }; // (4) 
bool operator == (EqualityComparable const&, EqualityComparable const&) { return true; }

struct NotEqualityComparable { }; // (5)

int main() {

 int myInt{};
 behavesLikeAnInt(myInt);
 behavesLikeAnInt2(myInt);

 std::vector<int> myVec{};
 behavesLikeAnInt(myVec);
 behavesLikeAnInt2(myVec);

 EqualityComparable equComp;
 behavesLikeAnInt(equComp);
 behavesLikeAnInt2(equComp);

 NotEqualityComparable notEquComp; 
 behavesLikeAnInt(notEquComp); // (6)
 behavesLikeAnInt2(notEquComp); // (7)
 
}

前のコード スニペットのすべての部分を組み合わせて、Regular (Zeile 1) の概念を取得します。関数 behaviorsLikeAnInt (行 2) と behaviors LikeAnInt2 (行 3) は、両方の概念を使用します。名前が示すように、EqualityComparable 型 (4 行目) は等価性をサポートしていますが、NotEqualityComparable 型 (5 行目) はサポートしていません。両方の関数 (6 行目と 7 行目) で型 NotEqualityComparable を使用するのが最も興味深い部分です。

GCC

プログラムの動作を確認したい場合は、Compiler Explorer へのリンクを使用してください:https://godbolt.org/z/XAJ2w3. GCC を使用した Compiler Explorer のエラー メッセージは非常に正確ですが、少し圧倒されます。これはおそらく、両方の概念が失敗し、概念がまだ実装の初期段階にあり、オンライン ツールがコンソールほど快適ではないという事実によるものです。

コンセプト レギュラー

基本的にこれは、Compiler Explorer を使用した、失敗した概念の正規 (6 行目) からのメッセージです。

コンセプト レギュラー

通常の C++20 コンセプト (7 行目) では、より精巧な実装が使用されています。その結果、より詳細なエラー メッセージが表示されました。

MSVC

ウィンドウのコンパイラのエラー メッセージが具体的ではありません。

次は?

これで、C++20 の概念に関するミニシリーズが終わりました。概念に関するあなたの意見を知りたいです。 概念は C++ の進化ですか?それとも革命ですか? 木曜日(06.02)を含めてメールをいただければ幸いです。次回のコンセプトの最終投稿で、皆さんの意見を紹介します。あなたの名前を言うべきときは、はっきり言ってください。