テンプレート

スポンサーリンク

型パラメータ

次の swap 関数は,数値型以外のデータ型について使うことはできません。
例えば,この swap 関数は string 型変数の値の交換には使うことができません。

void swap(double& a, double& b)
{
    double tmp = a;
    a = b;
    b = a;
}

次の swap 関数は,型パラメータ T を持ち,任意のデータ型について値の交換ができます。
このようなパラメータ T を持つ関数やクラスを,テンプレート (template) といいます。

template <class T>
void swap(T& a, T& b)
{
    T tmp = a;
    a = b;
    b = tmp;
}

int main()
{
    int m = 2, n = 3;
    swap(m, n);        // swap(int&a, int& b) と具体化される
}

パラメータ T を持つ関数やクラスの頭部には,template <class T> または template <typename T> と書きます (class と typename は同じ意味)。
複数のパラメータが必要であれば,template <class T, class U> のように書きます。

関数テンプレートの呼び出しは swap<int>(m, n) のように書きます。
ただし,引数から型の特定が可能な場合は,swap(m, n) のように省略できます。

値パラメータ

テンプレートには,型パラメータの他に,値パラメータを持たせることができます。

// 大きさ N の配列 arr を初期化
template <class T, int N>
void init(T (&arr)[N])
{
    for (int i = 0; i < N; ++i)
        arr[i] = T();
}

int main()
{
    int arr[256];
    init(arr);   // init(int (&arr)[256]) と具体化される
}

テンプレートのパラメータの値はコンパイル時に決定されます。
よって,値パラメータに渡す値はコンパイル時定数でなければなりません。

クラステンプレート

次のプログラムは,スタックを実現するテンプレートクラス Stack を定義したものです。
クラステンプレートについては,型の指定 (<int> など) を省略することはできません。

template <class T>
class Stack
{
private:
    T* stack;
    int index;
public:
    Stack(int size) : index(0) { stack = new T[size]; }
    ~Stack() { delete [] stack; }
    void push(T item) { stack[index++] = item; }
    T pop() { return stack[--index]; }
};

int main()
{
    Stack<int> stack(256);
    stack.push(123);
    stack.pop();
}

テンプレートの特殊化

特定の型に対して異なる処理を行いたい場合,次のようにテンプレートを特殊化します。
この例では,T = bool の場合にだけ,下に定義された add が呼び出されます。

template <class T>
T add(T a, T b) { return a + b; }

template <>
bool add(bool a, bool b) { return a || b; }

クラステンプレートの特殊化は次のように書きます。

template <class T>
class Class1 { ... };

template <>
class Class1<bool> { ... };

テンプレートメタプログラミング

次の Square のように,テンプレート引数からコンパイル時定数を計算するクラスを,メタ関数 (metafunction) といいます。
メタ関数はコンパイル時に評価されるため,コンパイル終了時点で Square<9>::value の値は 81 と求まっています。

template <int N>
struct Square
{
    enum { value = N * N };
};

int main()
{
    std::cout << Square<9>::value << std::endl;  // 出力: 81
}

次のプログラムは,同様の技法で階乗を求める Fact を定義したものです。

template <int N>
struct Fact
{
    enum { value = N * Fact<N - 1>::value };
};

template <>
struct Fact<0>
{
    enum { value = 1 };
};

int main()
{
    std::cout << Fact<10>::value << std::endl;  // 出力: 3628800
}
スポンサーリンク