どんな邪悪な魔法をしようとしているのか!?!
ハーブサッターとの質疑応答を聞いていましたが、コンセプトについての質問がありました。Herbは、コンパイラーの速度を低下させ(ソースは変更されないまま)、セクションはテンプレートのセクションよりも大幅に大きくなったと述べています。
なぜこれを行うのですか?コンセプトに関するドキュメントはどこにありますか?
どんな邪悪な魔法をしようとしているのか!?!
ハーブサッターとの質疑応答を聞いていましたが、コンセプトについての質問がありました。Herbは、コンパイラーの速度を低下させ(ソースは変更されないまま)、セクションはテンプレートのセクションよりも大幅に大きくなったと述べています。
なぜこれを行うのですか?コンセプトに関するドキュメントはどこにありますか?
注: 次の回答 (およびそれが回答する質問) は、概念の古い C++0x バージョンに関連しており、C++20 に追加された機能のバージョンとはほとんど関係がありません。
まず第一に、Herb は、概念自体がコンパイルを遅くしたとは言いませんでした。彼は、C++ 標準ライブラリを概念化すると、C++ 標準ライブラリを使用するすべてのコードのコンパイルが遅くなると述べました。
その理由はいくつかのことに帰着します。
1: テンプレートの制約にはコンパイル時間がかかります。
次のようにクラスを宣言すると、次のようになります。
template<typename T> class Foo {...};
コンパイラは Foo を解析するだけで、ほとんど何もしません。2 フェーズ ルックアップを使用しても、コンパイラはクラス Foo のコンパイルで多くのことを実行しません。もちろん、後で保存しますが、最初のパスは比較的高速です。
テンプレートをコンセプトで制約する場合:
template<ConceptName C> class Foo {...};
コンパイラはいくつかのことをしなければなりません。型のすべての使用がC
概念に準拠していることを事前に確認する必要がありConceptName
ます。これは、コンパイラがインスタンス化の時間まで延期した余分な作業です。
概念チェックが増えるほど、型が概念と一致することを確認するために費やすコンパイル時間が長くなります。
2: 標準 C++ ライブラリは多くの概念を使用します。
入力、出力、順方向、双方向、順次、連続など、反復子の概念の数に注目してください。そして、委員会はそれらをそれ以上に分解することを検討していました。多くのアルゴリズムには、さまざまな反復子の概念に対して複数のバージョンがあります。
そして、これには範囲の概念 (出力を除くイテレータの概念の種類ごとに 1 つある)、std::string の文字の概念、およびその他のさまざまな種類の概念は含まれません。これらはすべてコンパイルしてチェックする必要があります。
高速化するために本当に必要な概念はモジュールです。一連の事前チェック済みシンボルを含むモジュール ファイルをコンパイラが生成し、標準のコンパイル プロセスを実行せずにそのファイルを直接ロードする機能。解析からシンボル作成まで一直線。
注意: #include する .cpp ファイルごとに、コンパイラはそのファイルを読み取ってコンパイルする必要があります。これを行うたびにファイルは同じものですが、ファイルを忠実に読み取って処理する必要があります。概念化された について話している場合はstd::vector
、テンプレートのすべての概念チェックを行う必要があります。コンパイル時に行う標準的なシンボル検索のすべてを行う必要があります。などなど。
コンパイラがこれを行う必要がなかったら想像してみてください。一連のシンボルと定義をディスクから直接ロードできると想像してみてください。コンパイルはまったくありません。他のコードが使用するシンボルと定義を持ち込むだけです。
プリコンパイルされたヘッダーのようになります。プリコンパイル済みヘッダーは、.cpp ファイルごとに 1 つだけに制限されていますが、好きなだけモジュールを使用できます。
悲しいことに、モジュールは C++0x からプロセスのかなり早い段階でヤンクされました。また、モジュールがない場合、標準ライブラリを概念で制約すると、制約のないバージョンよりも常にコンパイルが遅くなります。
Herb がモジュールの目的を誤解していることに注意してください (この機能の最初の概念のほとんどは、彼が話していたものでした: クロスプラットフォーム DLL など)。それらの主な基本的な目的は、クロスプラットフォームの DLL を機能させることではなく、コンパイル時間を短縮することです。また、モジュール自体がクロスプラットフォームであることも意図していません。
この質問はかなり古く (2011 年から)、この記事の執筆時点 (2020 年) に概念が最近リリースされたので、人々を誤解させたり、概念の使用を思いとどまらせたりしないように、いくつかのことを明確にしたいと思います。
かつて考えられていた概念と、現在リリースされている概念は、まったく別の存在です。C++20 でリリースされたコンセプトは、コンセプトの初期設計と比較して機能が削減されているため、「コンセプト ライト」とも呼ばれます。それで、概念から何が取り除かれましたか?
主な違いは、概念の基本的な設計が、テンプレートの使用法の正確性だけでなく、このテンプレートの定義の正確性もチェックすることを目的としていたことです。たとえばAnimal
、メンバー関数が必要なtype のテンプレートがあるとしますmake_sound
。次のような制約付き関数テンプレートを想像できます。
template <typename Animal>
requires requires(Animal& x){
x.make_sound();
}
int animal_tricks(Animal& x) {
x.make_sound();
x.do_trick();
}
ここで、概念の初期設計では、必要な式の一部ではないメンバー関数を使用しているため、関数テンプレートの定義はanimal_tricks
正しくありません。do_trick
C++20 コンセプト ライトでは、このコンセプトの定義は問題ありません。コンパイラは、関数テンプレートの正確性をチェックしません。これanimal_tricks
は、コンセプト ライトの世界では、開発者が型の要件を正しく指定する必要があるためです。その違いは、コンパイル時間に大きな違いをもたらす可能性があります。2016 年には、コンセプトが C++17 に入る理由とそうでない理由を検討した 2 つの論文がありました
。
「なぜ概念が必要なのか、なぜ早くではなく後で来る必要があるのか。」</a> どちらもパフォーマンスを考慮に入れていなかったので、当時は問題がなかったことを示す良い指標です。
また、現在のコンセプト設計には、パフォーマンス上の利点がいくつかある可能性があります。Chiel の規則によると、コンパイルで最も遅いのは SFINAE です。これは、少なくとも (通常は) かなりの量の型をインスタンス化する必要があり、後でそれらを放棄するためです。コンセプト (実装方法によっては) は、テンプレートをインスタンス化する必要がない場合があり、実際にはパフォーマンス上の利点になる可能性があります。
ConceptsGCC Web サイトで役立つリソースを見つけることができます。それは、コンセプト(駄洒落を許してください)が実現可能かどうかを確認するために彼らが構築していたコンパイラ(GCCから分岐した)です。
費用は、あらゆる種類の言語構成要素に対して徹底的かつユビキタスで再帰的な妥当性チェックを実行する必要があることに起因すると思います。かなり豊富な制約セットを指定できるとすれば、それらのチェックには非常にコストがかかる可能性があります。
ちょっと悪夢バージョンの例外仕様!