すでにクラスとインターフェースがあるのに、なぜ概念(総称プログラミング)が考案されたのですか?


8

また、stackoverflow.com

STLの概念が存在する必要があり、実際にそれらがドキュメント化された(人間の)概念のみであり、現時点でC ++コードに変換できなかった場合に、それらを「クラス」または「インターフェース」と呼ぶのはばかげていると私は理解しています。しかし、言語を拡張して概念に対応する機会を与えられたとき、なぜそれらは単にクラスの機能および/または導入されたインターフェースを変更しなかったのですか?

概念はインターフェース(データのない100%抽象クラス)と非常に似ていませんか?それを見ると、インターフェースには公理のサポートが不足しているように見えますが、公理はC ++のインターフェースに導入されている可能性があります(概念を引き継ぐためのC ++でのインターフェースの仮定の採用を考えると)。このようなC ++インターフェイス(自動インターフェイスLessThanComparable、だれでも)に自動コンセプトでさえ簡単に追加できると思います。

concept_mapは、Adapterパターンとよく似ていませんか?すべてのメソッドがインラインの場合、アダプタは基本的にコンパイル時を超えて存在しません。コンパイラーは、インターフェースへの呼び出しをインライン化されたバージョンに置き換えるだけで、実行時にターゲットオブジェクトを直接呼び出します。

静的オブジェクト指向プログラミングと呼ばれるものを聞いたことがあります。これは、本質的に汎用プログラミングでオブジェクト指向の概念を効果的に再利用し、実行オーバーヘッドを発生させることなくOOPのパワーのほとんどを使用できることを意味します。なぜこのアイデアはさらに検討されなかったのですか?

これが十分に明確であることを願っています。私がそうでなかったと思うなら、私はこれを書き直すことができます。私に知らせて。

回答:


13

短い答え:コンパイル前コンパイル時に、目的に類似性のある概念が混在しています。インターフェイス(抽象クラ​​スとすべてのオブジェクト指向パラダイム実装)は、コンパイル時に適用されます概念は同じアイデアですが、汎用プログラミングのコンテキストでは、C ++ではコンパイル時の前に発生します。最後の機能はまだありません。

しかし、最初から説明させてください。


長い答え:

実際、コンセプトは単なる言語の強制であり、言語にすでに存在するものを「プログラマーにとってより簡単に」するものであり、「ダックタイピング」と呼ぶことができます。

をテンプレート関数に渡すとそれはコンパイラが呼び出されたときに実際の(インライン)コードを生成する汎用関数であり、そのにはテンプレートコードで使用されるいくつかのプロパティ(特性?)が必要です。したがって、ダックタイピングのアイデアですが、すべてコンパイル時に生成および実行されます

型に必要なプロパティがない場合はどうなりますか?

まあ、コンパイラーは、テンプレートから生成されたコードがコンパイルされて失敗した場合にのみ問題があることを認識します。つまり、生成されるエラーはテンプレートコード内のエラーとなり、プログラマーにエラーとして表示されます。また、テンプレートのコード生成の場合に提供されるメタ情報のため、エラーの情報は大量にあり、どのテンプレートのインスタンス化について話しているのかを知ることができます。

これに関するいくつかの問題:まず、ほとんどの場合、テンプレートコードはライブラリコードであり、ほとんどのプログラマーはライブラリコードのユーザーではなく、ライブラリコードのユーザーです。つまり、ライブラリがどのように記述されているか(デザインだけでなく、実際にどのように実装されているか)を理解していないと、この種の不可解なエラーを理解するのは非常に困難です。2番目の問題は、プログラマーがテンプレートコードを作成した場合でも、コンパイラーが問題が遅すぎると通知できるため、生成されたコードがコンパイルされているときに、失敗の理由がわかりにくい場合があることです。問題がタイププロパティに関連している場合は、コードを生成する前でもチェックする必要があります。

これがコンセプトで許可されている(および設計されている):(汎用コード)プログラマーがテンプレートパラメーターとして渡される型のプロパティを指定できるようにし、提供された型が要件を満たさない場合にコンパイラーが明示的なエラーを提供できるようにします要件。

チェックが成功すると、コードがテンプレートから生成され、コンパイルされます。

すべての概念チェックは、コンパイル時の前にのみ行われますオブジェクトのタイプではなく、タイプ自体をチェックします。コンパイル前のオブジェクトはありません。

さて、「インターフェース」について。

抽象または仮想基本型を作成すると、それを使用するコードで、実際の実装を知らなくても、子型のオブジェクトを操作できます。これを実施するために、基本型は仮想であり、子型によってオーバーロードされる可能性がある(またはオーバーロードされる必要がある)メンバーを公開します。

つまり、コンパイラは、基本クラスへの参照を必要とする関数に渡されるすべてのオブジェクトが1.基本クラスの子タイプの1つである必要があること、2。その子タイプの実装が必要であることをコンパイル時にチェックできることを意味します。存在する場合、基本クラスで宣言された仮想純関数。

したがって、コンパイル時に、コンパイラーはオブジェクトのタイプのインターフェースをチェックし、何かが欠落しているかどうかを報告します。

これはConceptsと同じアイデアですが、Conceptの説明で述べたように、遅すぎます。コンパイル時に発生します。汎用コード(テンプレートコード)を使用していないため、処理が完了し、仮想基本クラスでは公開できない型が汎用要件を満たしているかどうかを確認するのはもう遅すぎます。実際、C ++のオブジェクト指向パラダイムの実装全体は、テンプレートコードが処理されているときには存在しません。(まだ)オブジェクトはありません。それは

クラスは、オブジェクトを操作する関数の要件をチェックするために使用されるオブジェクトの制約を記述します。概念は、型(クラスを含む)の制約を記述し、これらの型と汎用コードの組み合わせから実際のコードを生成するための汎用コードの要件を確認するために使用されます。

つまり、やはり「健全性チェック」と同じですが、言語の別のレイヤーであるテンプレートです。テンプレートは、コンパイルされたコードに表示される前でもメタプログラミングやプログラミングタイプを可能にする完全な(チューリング完全な)言語です。コンパイラのスクリプトを書くのと少し似ています。それをスクリプト化できるとしましょう。クラスはスクリプトによって操作される単なる値です。現在、これらの値の制約を確認する方法は、明白ではない方法でスクリプトをクラッシュさせる以外にありません。概念はそれだけです。これらの値にタイプを提供します(生成されたコードではタイプです)。よくわからない...

仮想基本クラスとコンセプトのもう1つの非常に重要な違いは、最初のタイプはタイプ間の強い関係を強制し、それらを「血に縛られる」ようにすることです。テンプレートメタプログラミングでは「ダックタイピング」が可能ですが、コンセプトでは要件をより明確にすることができます。


4

クラスの宣言は「プログラミングオブジェクト」です。
コンセプトの宣言は「プログラミングクラス」です。

もちろん、常にプログラミングであるため、特定の類似点が見られますが、2つは抽象化プロセスの異なるフェーズに属しています。基本的に「クラス」(および「インターフェース」などのすべてがその周りにあります)は、実行時にエグゼキューターマシンがインスタンス化するオブジェクトを構造化する方法をコンパイラーに指示します。「概念」は、「クラス」が特定のコンテキストで「コンパイル可能」になるように構造化する方法をコンパイラーに指示することを意図しています。

もちろん、これらのステップを何度も繰り返すことは理論的には可能です

  • オブジェクト
  • オブジェクトのタイプ(クラス
  • (オブジェクトのタイプ)のタイプ(概念
  • タイプ((オブジェクトのタイプ)のタイプ)(???
  • .....

今、「コンセプトは、」C ++ 0xの仕様によって削除されている(彼らはまだいくつかの作業を必要とするため、および保持されたもう遅らせるためにそうではありませんでした)のアイデアコンセプトN場合、私は右回転NOW-を知りませんこれまでに役立つことができます。


これは概念的には理にかなっています。私はそれを理解しようとしていて、これが質問に答えるかどうかを考えています。どうもありがとう。
GuiPrá11年

3

あなたの質問のほとんどすべてに対する単純な答えは、「C ++コンパイラーは最低だから」です。真剣に。それらはCの翻訳ユニット技術に基づいて構築されており、多くの有用なものを効果的に禁止しており、既存のテンプレートの実装は、それがそうであるようにひどく遅いです。概念は、概念的な理由でカットされませんでした。信頼できる実装がなかったためにカットされました。ConceptGCCは非常に遅く、コンセプトの指定には、非常に長い時間がかかりました。Herb Sutterは、テンプレート全体を指定するよりも、標準ライブラリで使用される概念を指定する方がより多くのスペースを必要とすると述べました。

間違いなく、SFINAE decltypeとの間static_assertでは、いずれにせよ現在ほとんどが実装可能です。


2

私の理解では、インターフェイスとコンセプトは、C ++言語のさまざまな部分で同様の目的を持っています。

元の質問への回答で述べたように、インターフェイスの実装は、設計時にクラスの実装者によって決定されます。クラスが公開されると、設計時に派生したインターフェイスのみをサポートできます。

まったく同じメンバー関数とセマンティクス(つまり、同じ概念)を持つ2つの異なるインターフェースは、依然として2つの異なるインターフェースになります。両方のインターフェイスのセマンティクスをサポートする場合は、サポートを2回実装する必要がある場合があります。

それが、Generic Programmingが回避しようとしている問題です。C ++ Generic Programmingでは、テンプレートに渡されるタイプは、テンプレートで使用されるインターフェース(タイプの「プログラミングインターフェース」の意味で、大文字ではない)をサポートする必要があるだけです。同じメンバー関数を持つ2つの異なるインターフェイスは一致するため、コードを1回記述するだけで済みます。さらに、同じインターフェースをサポートするすべてのタイプ(明示的なインターフェースがない場合でも)が機能します。

これは2番目の問題につながります。インターフェイスが重複しているがセマンティクスが異なる 2つのタイプがある場合はどうなりますか?ジェネリックプログラミングでは違いを認識できず、すべて正しくコンパイルされますが、実行時の結果は驚くべきものであり、おそらく間違っているでしょう。

そこでコンセプトが登場します。大幅に単純化しすぎた場合は、コンセプトをインターフェイスのジェネリック(テンプレート)バージョンと見なすことができます。コンセプトから「派生」できる多数の潜在的なタイプに適用するには、1回だけ実装する必要があります。概念は、(クラス)タイプの事前定義されたセマンティックインターフェイスであり、そのタイプだけに限定されません。インターフェースとは異なり、2つの非常に異なるタイプ(ジェネリックコンパイラーに対して)は、基本クラスインターフェース、またはコンパイラーから見える実際のセマンティクスを持たない基本クラスインターフェースを制限する必要なしに、同じ概念を持つことができます。独自のものですが、タイプの区別にのみ使用されます。


ちょっと待って...コンセプトは一度実装されて、そこから「派生」できる多数の潜在的なタイプに当てはまるとあなたは言いました。つまり、すべての潜在的なタイプはそこから派生する必要があります。同じ内容の2つの概念が2つの異なるライブラリに存在し、自動マッピングがない場合、インターフェイスと同じ問題が発生します。同時に、インターフェースは自動マッピングをうまく機能させることができます。問題は私には同じように見えます。
GuiPrá11年

1
@ n2liquid-概念は、インターフェースと純粋なジェネリックプログラミングの利点と欠点の異なる組み合わせです。それらは厳密な改善ではありません。概念は、インターフェースの「所定の」部分を避けません。概念は、クラス型が同じセマンティクスをサポートするが、同じインターフェイスから派生できない状況を回避します(たとえば、インターフェイスがサブタイプdoubleに対して定義されている一方で、汎用バージョンはすべての数値型に適用されます)。
Joris Timmermans、2011
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.