ソース コードの編成 (C++ テンプレート)

クラス テンプレートを定義する場合、メンバーの定義がコンパイラから必要に応じて見えるようにソース コードを編成する必要があります。 使用できる選択肢には、"包含モデル" と "明示的なインスタンス化" モデルがあります。 包含モデルでは、テンプレートを使うすべてのファイルにメンバーの定義を含めます。 この方法は最もシンプルで、テンプレートで使える具象型について最大の柔軟性が得られます。 これの欠点は、コンパイル時間が増加する可能性があることです。 プロジェクトまたは含まれるファイル自体が大きい場合、時間は重要な場合があります。 明示的なインスタンス化の方法では、テンプレート自体によって具象クラスまたは特定の型のクラス メンバーがインスタンス化されます。 この方法によりコンパイル時間を短縮できますが、テンプレートの実装者が前もって有効にしていたクラスしか使えなくなります。 一般的に、コンパイル時間が問題にならない限りは、包含モデルを使うことをお勧めします。

背景

テンプレートは、コンパイラがテンプレートまたはそのメンバーのオブジェクト コードを生成しないという点で、通常のクラスとは異なっています。 テンプレートが具象型でインスタンス化されるまで生成するものはありません。 コンパイラによりテンプレートのインスタンス化 (MyClass<int> mc; など) が見つかり、そのシグネチャを持つクラスがまだ存在していない場合は、新しいクラスが生成されます。 また、使用されるメンバー関数のコードの生成も試行されます。 これらの定義が、コンパイル中の .cpp ファイルに直接または間接的に #included されていないファイル内にある場合、コンパイラはそれらを表示できません。 コンパイラの観点からは、必ずしもエラーであるとは限りません。 関数は、リンカーが検索する別の翻訳単位で定義できます。 リンカーがそのコードを見つけられない場合は、未解決の外部エラーが発生します。

包含モデル

翻訳単位全体からテンプレートの定義を見つけられるようにするための最も簡単で最も一般的な方法は、ヘッダー ファイル自体に定義を置くことです。 .cppテンプレートを使用するすべてのファイルは、ヘッダーに対して#include必要です。 この方法は、標準ライブラリで使用されます。

#ifndef MYARRAY
#define MYARRAY
#include <iostream>

template<typename T, size_t N>
class MyArray
{
    T arr[N];
public:
    // Full definitions:
    MyArray(){}
    void Print()
    {
        for (const auto v : arr)
        {
            std::cout << v << " , ";
        }
    }

    T& operator[](int i)
   {
       return arr[i];
   }
};
#endif

この方法を使うと、コンパイラから完全なテンプレートの定義にアクセスでき、必要に応じて任意の型のテンプレートをインスタンス化できます。 シンプルで比較的簡単にメインできます。 ただし、包含モデルではコンパイル時間に関するコストがかかります。 大規模なプログラム、特にテンプレート ヘッダー自体で他のヘッダーが #include されている場合は、このコストが深刻になる場合があります。 ヘッダーを #include しているすべての .cpp ファイルでは、関数テンプレートとすべての定義のコピーが独自に取得されます。 リンカーは通常、1 つの関数に対して複数の定義が作成されないように、処理を並べ替えることができますが、この作業を行うには時間がかかります。 小規模のプログラムでは、その余分なコンパイル時間はおそらく重要にはなりません。

明示的なインスタンス化モデル

包含モデルがプロジェクトに対して実行可能ではなく、テンプレートのインスタンス化に使用される型のセットがわかっている場合は、テンプレート コードを 1 つのファイルに.h.cpp分離し、ファイル内でテンプレートを.cpp明示的にインスタンス化できます。 この方法では、ユーザーのインスタンス化が発生したときにコンパイラに表示されるオブジェクト コードが生成されます。

明示的なインスタンス化を作成するには、キーワード (keyword)templateを使用し、その後にインスタンス化するエンティティのシグネチャを使用します。 このエンティティには、型またはメンバーを指定できます。 型を明示的にインスタンス化する場合は、すべてのメンバーがインスタンス化されます。

ヘッダー ファイル MyArray.h は、テンプレート クラス MyArrayを宣言します。

//MyArray.h
#ifndef MYARRAY
#define MYARRAY

template<typename T, size_t N>
class MyArray
{
    T arr[N];
public:
    MyArray();
    void Print();
    T& operator[](int i);
};
#endif

ソース ファイルMyArray.cppは明示的にインスタンス化され、template MyArray<string, 5>次のようになりますtemplate MyArray<double, 5>

//MyArray.cpp
#include <iostream>
#include "MyArray.h"

using namespace std;

template<typename T, size_t N>
MyArray<T,N>::MyArray(){}

template<typename T, size_t N>
void MyArray<T,N>::Print()
{
    for (const auto v : arr)
    {
        cout << v << "'";
    }
    cout << endl;
}

template MyArray<double, 5>;
template MyArray<string, 5>;

前の例では、明示的なインスタンス化はファイルの .cpp 一番下にあります。 MyArray は、double または String 型にのみ使用できます。

Note

C++ 11 では、テンプレートの定義のコンテキストにおいて export キーワードが非推奨になりました。 実際には、これの影響はほとんどありません。ほとんどのコンパイラでサポートされていないためです。