1. ホーム
  2. c++

[解決済み] 概念とテンプレート制約の違いは何ですか?

2023-01-01 05:49:44

質問

C++の完全な概念提案とテンプレート制約(例えば、Dlangで登場する制約や C++1y の新しい概念-ライトの提案 ).

テンプレート制約ができないことで、本格的な概念は何ができるのでしょうか?

どのように解決するのですか?

<ブロッククオート

以下の情報は古くなっています。最新のConcepts Liteドラフトに従って更新する必要があります。

の第3節 制約条件提案 はこれを合理的な深さでカバーしています。

コンセプトの提案 は、制約 (すなわち、概念ライト) をより短期間で具体化し、実装できるようにするため、しばらく後回しにされています。制約の提案は、後の概念の定義へのスムーズな移行として機能するように設計されています。制約とは の一部です。 の一部であり、その定義に必要なビルディングブロックです。

C++のための概念ライブラリの設計 では、Sutton と Stroustrup は次のような関係を考えています。

概念=制約条件+公理

その意味を簡単にまとめると

  1. 制約 - 型の静的に評価可能なプロパティに対する述語。純粋に構文的な要求。ドメインの抽象化ではありません。
  2. 公理 - 真であると仮定される型の意味的要件。静的にチェックされません。
  3. 概念 - アルゴリズムの引数に対する一般的、抽象的な要求。制約と公理の観点から定義されます。

つまり、制約(構文的性質)に公理(意味的性質)を加えると、概念になるわけです。


概念-ライト

concepts-lite の提案は、最初の部分である制約だけをもたらしますが、これは本格的な概念に向けた重要かつ必要なステップです。

制約

制約とは 構文 . コンパイル時に型の特性を静的に識別する方法を提供し、テンプレートの引数として使われる型を、その構文的特性に基づいて制限することができます。現在の制約の提案では、制約を命題法のサブセットで表現し、次のような論理的接続詞を使用しています。 &&|| .

制約の動作を見てみましょう。

template <typename Cont>
  requires Sortable<Cont>()
void sort(Cont& container);

という関数テンプレートを定義しています。 sort . 新しく追加されたのは requires 節です。 . requires節は、この関数のテンプレート引数に対するいくつかの制約を与えます。特に、この制約では、型 Cont でなければなりません。 Sortable の型でなければなりません。すっきりしたのは、より簡潔な形で次のように書けることです。

template <Sortable Cont>
void sort(Cont& container);

ここで、もしあなたが Sortable とみなされないものをこの関数に渡そうとすると、 すぐにエラーが発生して TSortable 型であることを示します。もしこれを C++11 でやっていたら、恐ろしいエラーが の内側 の中にある sort という、誰にとっても意味のない関数があります。

制約述語は型特徴に非常に似ています。それらはいくつかのテンプレート引数の型を取り、それについていくつかの情報を与えます。 制約は、型に関する以下のような質問に答えようとします。

  1. この型はそのような演算子をオーバーロードしているか?
  2. これらの型は、この演算子のオペランドとして使用することができますか?
  3. この型はこのような特性を持っていますか?
  4. この定数式はthatと等しいか?(非タイプのテンプレート引数の場合)
  5. この型は、その型を返すやまだという関数を持っているか?
  6. この型は、その型として使われるための構文的要件をすべて満たしているか?

しかし、制約とは を置き換えるものではありません。 型特性を置き換えるものではありません。その代わりに、それらは手を携えて働きます。いくつかの型特性は概念の観点から、いくつかの概念は型特性の観点から定義することができるようになりました。

制約の重要な点は、セマンティクスを全く気にしないことです。制約の良い例をいくつか挙げます。

  • Equality_comparable<T> : 型が == で、オペランドの両方がその同じ型であるかどうかを調べます。

  • Equality_comparable<T,U> : があるかどうかを調べます。 == があるかどうかを調べます。

  • Arithmetic<T> : 算術型であるかどうかを調べます。

  • Floating_point<T> : 型が浮動小数点型かどうかを調べます。

  • Input_iterator<T> : 入力イテレータがサポートしなければならない構文操作を型がサポートしているかどうかをチェックします。

  • Same<T,U> : 与えられた型が同じかどうかを調べます。

このすべてを試すには、特別な の概念ライトビルドで試すことができます。 .


ビヨンドコンセプト-ライト

さて、私たちはコンセプトライトの提案の先にあるものに入ります。これは、未来そのものよりもさらに未来的なことです。 ここから先のすべては、かなり変化する可能性があります。

公理

公理は、すべて セマンティクス . 公理は、関係、不変量、複雑さの保証、その他を指定します。例を見てみましょう。

がある一方で Equality_comparable<T,U> があることを教えてくれます。 operator== があり、それが型 TU のように、その操作が何であるかは教えてくれません。 . そのために、公理である Equivalence_relation . この公理は、この2種類のオブジェクトが比較されるときに operator== を与える true を与えると、これらのオブジェクトは等価となります。これは冗長に見えるかもしれないが、確かにそうである。あなたは簡単に operator== のような振る舞いをする operator< . そんなことをするのは邪道ですが、できるはずです。

別の例としては Greater の公理です。これは、2つのオブジェクトが型 T で比較することができます。 >< 演算子ですが、これらは何のために というのは ? その Greater の公理では、もし x が大きいなら y であれば yx . このような公理の仕様案は次のようなものである。

template<typename T>
axiom Greater(T x, T y) {
  (x>y) == (y<x);
}

つまり、公理は以下のような質問に答えているのです。

  1. これらの2つの演算子は互いにこの関係を持っていますか?
  2. この演算子でこのような型はこのような意味になりますか?
  3. そのような型に対するこの演算子はこのような複雑さを持つのでしょうか?
  4. その演算子のこの結果は、これが真であることを暗示していますか?

つまり、これらは型とその型に対する操作のセマンティクスに完全に関係しているのです。これらのことは静的にチェックすることはできません。もしこれをチェックする必要があるならば、型は何らかの方法でこれらのセマンティクスを遵守していることを宣言しなければなりません。

公理の一般的な例をいくつか紹介します。

  • Equivalence_relation : 二つのオブジェクトが比較される場合 == であれば、それらは等価である。

  • Greater : いつでも x > y であれば y < x .

  • Less_equal : いつでも x <= y であれば !(y < x) .

  • Copy_equality : の場合 xy 型の T : もし x == y の場合、コピーコンストラクションで作られた同じ型の新しいオブジェクトは T{x} == y であり、なおかつ x == y (になります(つまり、非破壊です)。

コンセプト

さて、概念を定義するのは非常に簡単です。 制約と公理の組み合わせです。 . 型のシンタックスとセマンティクスに対する抽象的な要求事項を提供します。

例として、次のようなものを考えてみましょう。 Ordered という概念を考えてみましょう。

concept Ordered<Regular T> {
  requires constraint Less<T>;
  requires axiom Strict_total_order<less<T>, T>;
  requires axiom Greater<T>;
  requires axiom Less_equal<T>;
  requires axiom Greater_equal<T>;
}

まず、テンプレート・タイプの T であること Ordered の要件を満たす必要があります。 Regular の概念に合致している必要があります。そのため Regular コンセプトは、型がうまく振る舞えること、つまり、構築、破壊、コピー、比較ができることを示す非常に基本的な要件です。

これらの要件に加え Ordered が要求しているのは T は1つの制約と4つの公理を満たしている必要があります。

  • 制約です。An Ordered 型には、必ず operator< . これは静的にチェックされるので が存在しなければなりません。
  • 公理です。については xy 型の T :
    • x < y は厳密な総順序を与える。
    • ときに x よりも大きい場合 y , yx よりも小さく、その逆も同様です。
    • いつ x よりも小さいか等しい y , yx よりも小さくはなく、その逆も同様です。
    • いつ x よりも大きいか等しい y , y よりも大きくはない x よりも大きくはなく、その逆も同様です。

このように制約と公理を組み合わせることで、概念が得られます。これらは、アルゴリズムで使用する抽象的な型に対する構文的および意味的な要件を定義します。アルゴリズムは現在、使用される型が特定の操作をサポートし、特定のセマンティクスを表現することを仮定しなければなりません。概念があれば、その要件が満たされていることを確認できるようになります。

最新のコンセプトデザイン の場合、コンパイラは概念の構文的要件がテンプレート引数によって満たされることだけをチェックします。公理はチェックされないままです。公理は静的に評価できない(あるいは完全にチェックできないことが多い)意味論を示すので、型の作成者は、その型が概念のすべての要件を満たしていることを明示的に表明しなければなりません。これは以前の設計ではコンセプト マッピングとして知られていましたが、現在では削除されています。

コンセプトの例を紹介します。

  • Regular の型は、構築可能、破壊可能、コピー可能であり、比較可能です。

  • Ordered タイプサポート operator< をサポートし、厳密な総順序とその他の順序付けのセマンティクスを持っています。

  • Copyable 型はコピーコンストラクタブル、デストラクタブル、そしてもし x と等しい場合は y であり x がコピーされると、そのコピーも y .

  • Iterator 型は関連する型を持っていなければなりません。 value_type , reference , difference_type そして iterator_category であり、それ自体がある概念を満たさなければならない。また、これらは operator++ をサポートし、再参照可能でなければなりません。

コンセプトへの道

制約は、C++ の完全な概念機能への最初のステップです。なぜなら、制約によって型の静的強制力が提供され、よりクリーンなテンプレート関数やクラスが書けるようになるからです。というのも、型に静的な強制力を持たせることで、テンプレート関数やクラスをよりきれいに書くことができるからです。 std::enable_if とそのメタプログラミングの仲間たちの困難さと醜さの一部を回避することができます。

しかし、制約の提案ではできないことがいくつもあります。

  1. 概念定義言語を提供しません。

  2. 制約はコンセプトマップではありません。ユーザーは、特定の制約を満たすように、その型を特に注釈する必要はありません。それらは単純なコンパイル時の言語機能を使って静的にチェックされます。

  3. テンプレートの実装は、そのテンプレート引数の制約によって制約されることはありません。つまり、関数テンプレートが制約された型のオブジェクトで、やってはいけないことをした場合、コンパイラはそれを診断する方法がありません。完全に機能する概念の提案では、これを行うことができるでしょう。

制約提案は、その上に完全な概念提案を導入できるように特別に設計されています。運が良ければ、この移行はかなりスムーズに行われるはずです。概念グループは、C++14(またはその直後の技術報告書)で制約を導入することを検討しており、完全な概念は C++17 の頃に現れ始めるかもしれません。