C++ テンプレートは、それらが使用される同じ変換単位 (.CPP ファイルと含まれるすべてのヘッダー ファイル) で定義する必要があります (完全な関数本体を指定)。ヘッダー ファイルで行ったすべてのことは、(関数の名前と署名を指定して)宣言されています。その結果、 を含めるbase.h
と、コンパイラは次のように認識します。
class Base {
template <typename T> void test(T a);
}
これは関数を宣言しますが、定義しません。それを定義するには、関数本体を含める必要があります。
class Base {
template <typename T> void test(T a)
{
// do something cool with a here
}
}
これが必要な理由は、C++ コンパイラが「必要に応じて」テンプレートのコードを生成するためです。たとえば、次のように呼び出す場合:
Base obj;
obj.test< int >( 1 );
obj.test< char >( 'c' );
Base::test
コンパイラは、テンプレートに基づいて 2 つのマシン コード セットを生成します。1つは 用でint
、もう 1 つは 用char
です。ここでの制限は、Base::test
テンプレートの定義が同じ翻訳単位 (.CPP ファイル) にある必要があることです。そうしないと、コンパイラはBase::test
関数の各バージョンのマシン コードをビルドする方法を認識できません。Base::test< T >
コンパイラは一度に 1 つの翻訳単位でしか動作しないため、他の CPP ファイルで定義したかどうかはわかりません。手元にあるものでのみ機能します。
これは、C#、Java、および同様の言語でジェネリックが機能する方法とはまったく異なります。個人的には、テンプレートを、必要に応じてコンパイラによって展開されるテキスト マクロと考えるのが好きです。そのため、テンプレート関数の完全な本体を、それが使用されるすべての CPP ファイルに含める必要があることに留意する必要があります。