まず、テンプレートのメカニズムを理解する必要があります。テンプレートはコンパイルされません。テンプレートは使用時にインスタンス化され、その後インスタンス化がコンパイルされます。したがって、コンパイラは、渡されたパラメーターに従って最初にインスタンス化するために、テンプレート関数を使用して各モジュールに完全なテンプレート定義を含める必要があります。
問題を解決するには 3 つの解決策がありますが、どちらも同じ結果になることがわかります。クラス定義内のヘッダー ファイルにテンプレート全体を実装します (テンプレート定義が含まれていることを正確に示すために、.h の代わりに .hxx をサフィックスとして使用します)。
// Foo.hxx
#ifndef __FOO_HXX__
#define __FOO_HXX__
class Foo {
public:
template <class T>
void bar(const T& t) {
t.doSomething();
}
};
#endif
または、クラスから定義を外部化することもできますが、ヘッダー ファイル内に残します。
// Foo.hxx
#ifndef __FOO_HXX__
#define __FOO_HXX__
class Foo {
public:
template <class T>
void bar(const T&);
};
template <class T>
void Foo::bar(const T& t) {
t.doSomething();
}
#endif
最後に、テンプレート メソッド本体を外部ファイルに実装できます (同じ理由で .cxx のプレフィックスが付いています)。メソッドの本体は含まれますが、「Foo.hxx」は含まれません。代わりに、クラス定義の後に「Foo.cxx」をインクルードするのは「Foo.hxx」です。このようにして、コンパイラは #include ディレクティブを解決するときに、同じモジュール内のテンプレート定義全体を見つけ、それをインスタンス化できるようにします。
// Foo.hxx
#ifndef __FOO_HXX__
#define __FOO_HXX__
class Foo {
public:
template <class T>
void bar(const T&);
};
#include "Foo.cxx"
#endif
// Foo.cxx
template <class T>
void Foo::bar(const T& t) {
t.doSomething();
}
テンプレートを実装するこれら 3 つの方法の選択は、むしろ読みやすさ (および好み) の問題です。
2 番目と 3 番目は、生成されたコードの点では同等ですが、インクルードを反転するのを忘れると愚かなエラーが発生することが多いため、cxx ファイル ソリューションは使用しません。
さらに、STL や Boost などのよく知られた C++ ライブラリは、ヘッダー ファイルのみでコードを提案します。これは、優れた設計の兆候です。ヘッダー内で外部定義を使用することにより、クラスの定義を明確にします。また、Herb Sutter http://www.gotw.ca/gotw/033.htmによると、コンパイラがメソッドを自動的にインライン化するのを防ぎます。これにより、結果が悪くなることがあります。