テンプレートは本質的に半型安全なマクロであるため、制限があります。
通常の (非テンプレート) 関数は、オブジェクト/ライブラリ ファイルに存在するネイティブ コードにコンパイルでき、ヘッダーで利用可能なプロトタイプのみを使用して参照できます。これは、そのような関数のバージョンが 1 つしかないためです。
テンプレートを使用すると、C++ コンパイラは関数の各インスタンスを個別にコンパイルする必要があります。明らかに、関数をインスタンス化できる型のセットは事実上無制限であるため、「事前に」行うことはできません (関数を呼び出す前にコードでいつでも新しい型を定義できます)。実際、同じ関数テンプレートの 2 つのインスタンス化が完全に異なる場合があります。この極端なケースを考えてみましょう:
struct t1 {
template <int>
struct a {};
};
struct t2 {
enum { a = 123 };
};
enum { b = 456, c = 789 };
template <class T>
void foo() {
T::a<b>c;
}
を呼び出すとfoo<t1>()
、その中のステートメントはローカル変数宣言になります。これt1::a
はクラス テンプレートであるためです。
T::a<b> c;
しかし、 を呼び出すと、は整数定数foo<t2>()
であるため、内部のステートメントは式になります。t2::a
(T::a < b) > c;
これは、コンパイラがテンプレートを意味のある「コンパイル」できないことを示すためのものです。実際には、ほとんどのトークンを保持する必要があります。
とはいえ、ISO C++ はテンプレートの宣言と定義を分離する機能を実際に提供しているため、.h ファイルの宣言と .cpp ファイルの定義を使用して、テンプレートを通常の関数として扱うことができます。これは、宣言と定義の両方の前にキーワードを付ける必要があるため、「テンプレートのエクスポート」と呼ばれますexport
。
// link.h
export template <class T>
T *Link(T *&, T *(*)());
// link.cpp
export template <class T>
T *Link(T *&ChildNodeReference, T *(*ObjectCreator)()) {
}
ただし、これは実装の負担が非常に大きいため、標準の物議を醸す機能であり、最も一般的な実装では実装を拒否しています。特に、g++、MSVC、および C++Builder はそれを実装していません。それをサポートしている私が知っている唯一のコンパイラはComeau C++です。