特殊化¶
テンプレートでは実引数に応じて関数やクラスを生成します。
// 関数テンプレート template <typename T> T Sum(T a, T b) { return a + b; } // 関数テンプレートの関数の呼び出し Sum<int>(1, 2);
Sum<int>(1, 2)
という関数テンプレートの関数の呼び出しによって
T
が int
である関数が必要と判断され、次の関数が生成されます。
int Sum(int a, int b) { return a + b; }
このようにテンプレートを使用する箇所において、 関数テンプレートから関数を生成することおよび クラステンプレートからクラスを生成することを特殊化 (または暗黙的インスタンス化) といいます。
特殊化はコンパイラによって行われるため、 ヘッダファイルで関数テンプレートを使用する場合にはそのヘッダファイルで定義も行います。
#ifndef SUM_H_ #define SUM_H_ template <typename T> inline T Sum(T a, T b) { // inline 指定が必要 return a + b; } #endif // SUM_H_
#include <iostream> #include "sum.h" int main() { std::cout << Sum(1, 2) << std::endl; return 0; }
テンプレートの明示的インスタンス化
テンプレートを使用する箇所で関数やクラスを生成する代わりに、 ソースファイルで明示的に関数やクラスを生成することで ヘッダファイルでは宣言だけ行うことは可能ではあります。
#ifndef SUM_H_ #define SUM_H_ template <typename T> T Sum(T a, T b); // 宣言だけ行う (inline もつけない) #endif // SUM_H_
#include "sum.h" // 関数テンプレートの定義 template <typename T> T Sum(T a, T b) { return a + b; } // 明示的インスタンス化 template int Sum<int>(int, int);
こうした構成にするとヘッダファイルでテンプレートが提供されていても
使用可能な型はソースファイルで明示的な生成を行う型のみとなってしまいます。
たとえば Sum<double>(double, double)
は生成されていないため
Sum(1.2, 3.4)
のように関数テンプレートの関数を呼び出すとリンクエラーになります。
こうした問題を避けるためには、 ヘッダファイルでは関数テンプレートをやめてオーバーロードされた関数を提供し、 ソースファイルで関数テンプレートを使用します。
#ifndef SUM_H_ #define SUM_H_ int Sum(int a, int b); double Sum(double a, double b); #endif // SUM_H_
#include "sum.h" // 実装に関数テンプレートを使用 template <typename T> T SumImpl(T a, T b) { return a + b; } int Sum(int a, int b) { return SumImpl(a, b); } double Sum(double a, double b) { return SumImpl(a, b); }
完全特殊化¶
特殊化によって関数テンプレートやクラステンプレートから関数やクラスを生成する代わりに、 通常の関数やクラスを使用するように指定することで 特定のテンプレート引数に対する挙動を変更することができます。 これを完全特殊化 (または明示的特殊化) といいます。
関数テンプレートの完全特殊化は次のようにします。
template <typename T> T DoSomething(T a, T b) { return a + b; } template <> double DoSomething<double>(double a, double b) { return a * b; } std::cout << DoSomething(2, 3) << std::endl; // 5 std::cout << DoSomething(2.0, 3.0) << std::endl; // 6
関数の前に template <>
を付けて完全特殊化を行うことを指定し、
関数名の後に < ... >
で対象となるテンプレート引数を指定します。
クラステンプレートの完全特殊化も同様です。
template <typename T> class Array { public: explicit Array(int size) : size_(size), data_(new T[size_]) {} ~Array() { delete[] data_; } int Size() const { return size_; } private: const int size_; T* data_; }; template <> class Array<bool> { public: explicit Array(int size) : size_(size), data_size_((size - 1) / 8 + 1), data_(new uint8_t[data_size_]) {} ~Array() { delete[] data_; } int Size() const { return size_; } private: const int size_; const int data_size_; uint8_t* data_; };
この例では8個の bool
値を1個の uint8_t
で扱って省メモリ化するために、
bool
に対する完全特殊化を行っています。
部分特殊化¶
特定のテンプレート引数に対して 特殊化で使用する関数テンプレートやクラステンプレートを別のテンプレートに変更することができます。 これを部分特殊化といいます。
詳細は テンプレートの部分特殊化 - cppreference.com を参照してください。