ConceptsはC++0x標準を作成しませんでしたが、BoostはBoost Concept Check Library(BCCL)を提供します。BCCLは、C++0x標準に含まれることを意図したすべてを網羅しているわけではないと思います。BCCLと提案されたC++0xソリューションの違いは何ですか?
3 に答える
テンプレート定義の確認
これらの手動ソリューションとの概念の大きな違いは、概念により、特別なことを何もせずにテンプレートの定義を型チェックできることです。コンセプトチェックライブラリでは、その*使用*のみをタイプチェックできます(手動でテストインスタンス化タイプを記述したり、標準の場合に提供されたタイプを使用したりしない限り、以下を参照してください)。例:template<typename InputIterator>
int distance(InputIterator a, InputIterator b)
{ return b - a; }
これで、そのテンプレートにコンセプトチェックと特性を振りかけることができますが、そのテンプレートを記述した後にエラーが発生することはありません。これは、標準では、コンパイラがインスタンス化までテンプレートのコンパイルを遅らせることができるためです。チェックするには、「アーキタイプ」クラスを作成する必要があります。このクラスには、インターフェイスに必要な操作が正確に含まれており、それらを人為的にインスタンス化します。
BCCLのドキュメントを読んで、「デフォルトの構成可能」のような一般的なアーキタイプがすでに含まれていることがわかりました。ただし、独自の概念を作成する場合は、独自のアーキタイプも提供する必要があります。これは簡単ではありません(タイプが提供する必要のある最小限の機能を正確に見つける必要があります)。たとえば、アーキタイプにが含まれている場合、operator-
その(誤った)アーキタイプを使用したテンプレートのテストは成功しますが、概念にはそのような演算子は必要ありません。
拒否された概念の提案は、指定され、暗示されている要件に基づいて、アーキタイプを自動的に作成します(たとえばT*
、パラメーターで使用されるポインタータイプは、のPointeeType要件を暗示しますT
)。もちろん、テンプレート定義に型エラーが含まれている場合を除いて、このようなことを気にする必要はありません。
セマンティック要件の確認
架空の概念チェックを使用して、このコードを検討してください
template<ForwardIterator I>
void f(I a, I b) {
// loop two times!
loopOverAToB(a, b);
loopOverAToB(a, b);
}
BCCLマニュアルには、セマンティック要件はチェックされていないと記載されています。構文要件とタイプのみがチェックされます。フォワードイテレータについて考えてみましょう。マルチパスアルゴリズムで使用できるというセマンティック要件があります。構文チェックでは、この要件をテストすることはできません(ストリームイテレータが誤ってそのチェックに合格した場合にどうなるかを考えてください!)
auto
却下された提案では、構文チェック後にコンパイラフラグを成功させるために、概念定義の前に明示的に配置する必要がありました。指定されていない場合auto
、タイプは、その概念をサポートしていると言うために、概念マップを明示的に定義する必要がありました。したがって、ストリームイテレータがForwardIteratorチェックに合格することはありません。
構文の再マッピング
これは別の機能でした。次のようなテンプレートtemplate<InputIterator I>
requires OutputStreamable<I::value_type>
void f(I a, I b) {
while(a != b) std::cout << *a++ << " ";
}
ユーザーが整数を逆参照する方法、つまり整数がInputIteratorの概念を満たす方法をコンパイラーに教える概念マップを提供する場合は、次のように使用できます。
f(1, 10);
これは言語ベースのソリューションの利点であり、BCCLでは解決できないと私は信じています。
コンセプトベースのオーバーロード
BCCLをざっと読んだだけでも、これを可能にするものを見つけることはできません。コンセプトマッチングの失敗は、ハードコンパイルエラーを引き起こすようです。却下された提案では、次のことが許可されます。template<ForwardIterator I>
I::difference_type distance(I a, I b) {
I::difference_type d = 0; while(a != b) ++a, ++d;
return d;
}
template<RandomAccessIterator I>
I::difference_type distance(I a, I b) {
return b - a;
}
タイプを両方のテンプレートで使用できる場合は、2番目のテンプレートが使用されます。これは、より専門的であるためです。つまり、概念RandomAccessIterator
を洗練します。ForwardIterator
C++0x コンセプト機能はコア言語機能であり、そのプロセス全体がコンパイラによって実行されます。
Boost Concept Check Library はほぼ同じ機能ですが、機能の一部をシミュレートするためのライブラリとして C++ とマクロで記述されています。最終的な言語機能 (最終的な機能定義によって異なります) で必要となるすべてのことを行うことはできませんが、テンプレートの型チェック (およびその他のコンパイル時のチェック) に対していくつかの同等のソリューションを提供します。
示唆されているように、C++0x の概念は言語機能であるため、より洗練されたセマンティックを提供し、コンパイラがプログラムで現在利用できない情報を使用できるようにし、コンパイル時により詳細またはインテリジェントなエラーを許可します (最初の概念として)。目的は、テンプレートで抽象型チェックを許可することです)。
免責事項: Boost を既にインストールしていたにもかかわらず、過去 30 分以内に BCCL を正常に使用できませんでした。以下の例は、Boost 1.37 の BCCL ドキュメントによると問題ないように見えますが、機能しませんでした。これはデメリットに数えられると思います。
BCCL を使用すると、静的アサーションのようなものしか得られませんが、コア言語コンセプト機能は完全なモジュラー型チェックを提供し、一部の関数テンプレートがオーバーロード解決に参加するのを防ぐことができます。ネイティブの概念では、制約されたテンプレートの本体はコンパイラによってすぐにチェックできますが、BCCL ではコンパイラにその点で何もチェックさせません。「arche type」パラメーターを使用してテンプレートを手動でインスタンス化し、テンプレートが使用できない操作 (たとえば、前方イテレーターでの operator--) を使用しているかどうかを確認する必要があります。
過負荷の解決については、次の例があります。
template<typename Iter>
void foo(Iter,Iter) {
BOOST_CONCEPT_ASSERT((RandomAccessIterator<Iter>));
}
void foo(long, int);
int main() {
foo(2,3); // compile-time error
}
非テンプレートでは int から long への変換が必要なため、テンプレートの方が適しています。しかし、int は反復子ではないため、インスタンス化は失敗します。それを説明する素敵なエラーメッセージが表示されますが、それはあまり満足のいくものではありませんか? ネイティブの概念を使用すると、記述できた可能性があります
template<typename Iter>
requires RandomAccessIterator<Iter>
void foo(Iter,Iter) {}
void foo(long, int);
int main() {
foo(2,3); // OK, picks non-template foo
}
ここでは、T=int の要件が満たされていないため、関数テンプレートはオーバーロードの解決に参加しません。すごい!
SFINAE トリックを使用して関数テンプレートを制限することもできます。C++0x は SFINAE を式に拡張し、関数テンプレートの decltype およびデフォルトのテンプレート引数とともに、記述できる
template<typename T> T&& make();
template<typename Iter, class = decltype( *make<Iter>() )>
void foo(Iter,Iter) {}
void foo(long, int);
int main() {
foo(2,3); // OK, picks non-template foo
}
この場合、コンパイラは式 ∗make<Iter>() の型が何であるかを知らないため (int を逆参照することはできません) 、テンプレート引数推定は暗黙のうちに失敗します。少しのテンプレート メタプログラミングといくつかのマクロを使用すると、テンプレート パラメータに任意の構造的制約を読みやすい方法で課すことにかなり近づけることができます。REQUIRES と RandomAccessIterator の適切な定義を仮定すると、次のようになります。
template <typename Iter
REQUIRES( RandomAccessIterator<Iter> )
>
void foo(Iter,Iter) {}
HTH、S