7

(注:違法であることは知っています。言語がそうする理由を探しています。)

template<class c> void Foo();  // Note: no generic version, here or anywhere.

int main(){
  Foo<int>();
  return 0;
}

template<> void Foo<int>();

エラー:

error: explicit specialization of 'Foo<int>' after instantiation

Google との簡単なパスで、この仕様の引用が見つかりましたが、それは理由ではなく、何を提供するだけです。

編集:

いくつかの応答は、ルールがこのようになっているという議論を進めています (たとえば、私の推測を確認しました)。そうしないと、One Definition Rule (ODR)に違反するからです。ただし、この場合、次の 2 つの理由で成立しないため、これは非常に弱い議論です。

  1. 明示的な特殊化を別の翻訳単位に移動すると、問題が解決し、ODR に違反していないように見えます (リンカーがそう言っています)。
  2. (関数に適用される) ODR の短い形式は、任意の関数に対して複数の本体を持つことはできないということです。私はそうではありません。関数の本体が定義される唯一の場所は明示的な特殊化でFoo<int>あり、特殊化する汎用本体がないため、 への呼び出しはテンプレートの汎用特殊化を定義できません。

この件に関する憶測:

ルールが存在する理由についての推測: 最初の行が (宣言ではなく) 定義を提供した場合、複数の定義を取得するため、インスタンス化後の明示的な特殊化が問題になります。しかし、この場合、目に見える唯一の定義は明示的な特殊化です。

奇妙なことに、次の(または私が取り組んでいる実際のコードのようなもの)が機能します:

ファイル A:

template<class c> void Foo();

int main(){
  Foo<int>();
  return 0;
}

ファイル B:

template<class c> void Foo();

template<> void Foo<int>();

しかし、一般的にそれを使用すると、スパゲッティのインポート構造が作成されます。

4

3 に答える 3

5

ルールが存在する理由についての推測: 最初の行が (宣言ではなく) 定義を提供した場合、複数の定義を取得するため、インスタンス化後の明示的な特殊化が問題になります。しかし、この場合、目に見える唯一の定義は明示的な特殊化です。

しかし、複数の定義があります。Foo< int > をインスタンス化するときに既に定義しており、その後、すでに定義されている int のテンプレート関数を特殊化しようとしています。

int main(){
  Foo<int>();    // Define Foo<int>();
  return 0;
}

template<> void Foo<int>(); // Trying to specialize already defined Foo<int>
于 2011-05-03T17:46:14.263 に答える
2

明示的な特殊化を関数宣言と考える必要があります。2つのオーバーロードされた関数(テンプレート化されていない)がある場合と同様に、2番目のバージョンを呼び出そうとする前に、宣言が1つしか見つからない場合、コンパイラーは、必要なオーバーロードされたバージョンが見つからないと言います。テンプレートとの違いは、コンパイラが一般的な関数テンプレートに基づいてその特殊化を生成できることです。では、なぜこれを行うことが禁止されているのですか?完全なテンプレートの特殊化は、見たときにODRに違反しているため、その時点で、同じタイプのテンプレートの特殊化がすでに存在しているためです。テンプレートがインスタンス化されると(暗黙的かどうかに関係なく)、対応するテンプレートの特殊化も作成されます。後で(同じ翻訳単位で)同じスペシャライゼーションを使用すると、インスタンス化を再利用でき、インスタンス化ごとにテンプレートコードを複製する必要がなくなります。明らかに、ODRは、他の場所に適用されるのと同じように、テンプレートの特殊化にも適用されます。

したがって、引用されたテキストに「診断は不要」と記載されている場合、問題は明示的な特殊化の前に発生するテンプレートのインスタンス化が原因であるという洞察に満ちたコメントをコンパイラが提供する必要がないことを意味します。しかし、それが行われない場合、他のオプションは、標準のODR違反エラー、つまり「[T=int]の「Foo」特殊化の複数の定義」などを与えることです。より賢い診断として役立ちます。

編集への応答

1)ことわざには、すべてのテンプレート関数定義(つまり実装)がインスタンス化の時点で表示されている必要があります(コンパイラーがテンプレート引数を置き換えて関数テンプレートをインスタンス化できるようにするため)。ただし、関数テンプレートの暗黙的なインスタンス化には、関数の宣言が使用可能である必要があるだけです。したがって、あなたの場合、それを2つの変換単位に分割することは機能します。これは、ODRに違反しないため(そのTUではの宣言が1つしかないため)、暗黙のインスタンス化ポイントで使用可能なFoo<int>場合の宣言(から)、およびの定義Foo<int>Foo<T>Foo<int>TU B内のリンカが利用できます。したがって、この2番目の例が「機能しない」と主張する人は誰もいません。これは、想定どおりに機能します。あなたの質問は、1つの翻訳単位内に適用されるルールについてです。2つのTUに分割した場合(特に、2つのTUで明らかに正常に機能するはずの場合)、エラーは発生しないと言って、議論に反論しないでください。ルール)。

2)最初の例では、コンパイラが一般関数テンプレート(特殊化されていない実装)を見つけることができず、したがってFoo<int>一般テンプレートからインスタンス化できないため、エラーが発生します。または、コンパイラーは一般的なテンプレートの定義を見つけ、それを使用してインスタンス化しFoo<int>、2番目のテンプレートの特殊化Foo<int>が検出されたためにエラーをスローします。あなたは、コンパイラがあなたの専門分野に到達する前にそれを見つけるだろうと思っているようですが、そうではありません。C ++コンパイラは、コードを上から下にコンパイルします。あちこちで何かを置き換えるために行ったり来たりすることはありません。コンパイラがの最初の使用にFoo<int>到達すると、その時点で一般的なテンプレートのみが表示され、インスタンス化に使用できるその一般的なテンプレートの実装があると想定されます。Foo<int>、それはのための特別な実装を期待していません、それFoo<int>は一般的なものを期待して使用します。次に、特殊化を確認してエラーをスローします。これは、一般バージョンを使用することをすでに認識しているため、同じ関数に対して2つの異なる定義が表示され、ODRに違反しているためです。それはそれと同じくらい簡単です。

なぜああなぜ!!!

2 TUの場合は、C ++の機能であるTU間でテンプレートのインスタンス化を共有できる必要があるため、機能する必要があります。インスタンス化の可能性が少ない場合は、それらをプリコンパイルできます。

C ++で何かを宣言すると、コンパイラに「このことはどこかに定義されている」と通知されるため、 1TUの場合は許可されません。コンパイラに「テンプレートの一般的な定義がどこかにある」と言い、次に「一般的な定義を使用して関数を作成したい」とFoo<int>言い、最後に「呼び出されるたびFoo<int>にこの特別な定義を使用する必要がある」と言います。それは完全な矛盾です!だからですODRは存在し、このコンテキストに適用され、そのような矛盾を禁止します。「検出される」という一般的な定義が存在しないかどうかは関係ありません。コンパイラーはそれを予期し、存在し、特殊化とは異なると想定する必要があります。先に進んで「わかりました。コード内の他の場所で一般的な定義を探します。見つからない場合は、戻って、この特殊化を一般的なものの代わりに使用することを「承認」します。定義ですが、それが見つかった場合は、この特殊化にエラーのフラグを付けます。」また、後で表示される特殊化を使用するコードに対して、一般的なテンプレートを使用する意図を明確に示すコードを変更することによって、プログラマーの要望を完全に無視することもできません(特殊化はまだ宣言されていないため)。私は出来ます'

2TUの場合は完全に異なります。コンパイラがTUA(を使用する)をコンパイルしているときFoo<int>、一般的な定義を探し、それを見つけることができず、後でとしてリンクインされると想定しFoo<int>、シンボルプレースホルダーを残します。次に、リンカはテンプレートを検索しないため(実際には、テンプレートはエクスポートできません)、を実装する関数を検索し、Foo<int>それが特殊なバージョンであるかどうかは関係ありません。リンクする同じシンボルが見つかった場合、リンカーは問題ありません。これは、プログラマー(コンパイルされたライブラリーの関数を簡単に変更できない)とコンパイラーベンダー(実装する必要がある)の両方にとって、そうでなければ(「エクスポートされたテンプレート」に関する議論を調べる)悪夢になるためですこのリンククレイジースキーム)。

于 2011-05-03T18:03:15.273 に答える
2

インスタンス化の後に明示的な特殊化が表示されるため、このコードは不正です。基本的に、コンパイラーは最初にジェネリックテンプレートを確認し、次にそのインスタンス化を確認して、そのジェネリックテンプレートを特定のタイプに特化しました。その後、すでにインスタンス化されているジェネリックテンプレートの特定の実装を確認しました。では、コンパイラは何をすることになっていますか?戻ってコードを再コンパイルしますか?そのため、それは許可されていません。

于 2011-05-03T17:52:31.810 に答える