3

次のファイルを検討してください。

フー.H

template <typename T>
struct Foo
{
  int foo();
};

template <typename T>
int Foo<T>::foo()
{
  return 6;
}

Foo.C

#include "Foo.H"

template <>
int Foo<int>::foo()
{
  return 7;
}

main.C

#include <iostream>
#include "Foo.H"

using namespace std;

int main()
{
  Foo<int> f;
  cout << f.foo() << endl;
  return 0;
}

コンパイルして実行すると、7 が出力されます。何が起きてる?テンプレートはいつインスタンス化されますか? コンパイラがそれを行う場合、コンパイラは独自のバージョンの Foo をインスタンス化しないことをどのように知るのでしょうか?

4

4 に答える 4

17

問題は、1 つの定義ルールに違反したことです。main.C には Foo.H が含まれていますが、Foo.C は含まれていません (ソース ファイルであるため、これは理にかなっています)。main.C がコンパイルされると、コンパイラはテンプレートが Foo.C に特化されていることを認識しないため、汎用バージョン (6 を返す) を使用して Foo クラスをコンパイルします。次に、Foo.C をコンパイルすると、すぐにコンパイルできる完全な特殊化が表示されます。すべての型が入力されているため、どこかでインスタンス化されるのを待つ必要はありません (2 つのテンプレート パラメーターがあり、特殊化されたものはそうではありません)、新しい別のFoo クラスをコンパイルします。

通常、同じものを複数定義するとリンカ エラーが発生します。ただし、テンプレートのインスタンス化は「弱いシンボル」です。つまり、複数の定義が許可されます。リンカーは、すべての定義が実際には同じであると想定し、ランダムに 1 つを選択します (まあ、おそらく一貫して最初のものか最後のものですが、実装の偶然としてのみ)。

なぜそれらを弱いシンボルにするのですか?Foo は複数のソース ファイルで使用される可能性があり、それぞれが個別にコンパイルされ、コンパイル単位で Foo が使用されるたびに新しいインスタンス化が生成されるためです。通常、これらは冗長であるため、破棄するのが理にかなっています。しかし、一方のコンパイル単位 (foo.C) で特殊化を提供し、もう一方 (main.C) では提供しないことで、この仮定に違反しています。

Foo.H でテンプレートの特殊化を宣言すると、main.C がコンパイルされるときに Foo のインスタンス化が生成されないため、プログラムに定義が 1 つだけ存在するようになります。

于 2010-01-13T16:22:26.563 に答える
2

コンパイラは Foo をインスタンス化すると思いますが、リンク時に代わりに特殊化された Foo を選択します。

于 2010-01-13T16:19:00.970 に答える
1

main.cをコンパイルするとき、コンパイラはあなたの専門分野を知りません。Foo<int>::foo()特殊化されていないテンプレートに基づいて、独自のバージョンを生成する必要があると思います。

ただし、リンクすると、リンカはの特殊化がFoo<int>::foo()存在することを確認します。したがって、実行可能ファイルに専用バージョンを入れます。

結局、コンパイル時にそれを知らなくても、main.cは特殊バージョンのを呼び出しますFoo<int>::foo()

于 2010-01-13T16:24:17.357 に答える
-2

テンプレートは、テンプレート パラメーターの組み合わせごとに異なるクラスを生成します。これはコンパイル時に発生するため、テンプレートをヘッダーに配置する必要があります。int パラメーターの特殊化を行い、コンパイラーがFoo<int>::foo()variable を呼び出しますf。仮想関数をオーバーライドするようなものですが、コンパイル時に行われます。

于 2010-01-13T16:11:15.183 に答える