C++ や D言語のテンプレートと呼ばれる機能のうち、クラスの雛形となるもののことです。
テンプレートは、静的型付け言語において、型を抽象化したソースコードを記述するための機能です。
クラステンプレートは、クラスのメンバが使う型を抽象化し、ソースコードを共通化します(C++ では class と struct は実質同じなので、構造体に対しても同じことが可能です)。
たとえば、任意の型の値を保持できるクラスを C++ では次のように記述できます。
template <typename T> // テンプレートであることを示す記述
class DataStore {
public:
(T data) : mData(data)
DataStore{
}
inline T Get() const
{
return mData;
}
private:
;
T mData};
int
のような具体的な型の指定の代わりに T
という型名を使うことで抽象化しています。この T
はテンプレート仮引数と呼ばれ、クラステンプレートを使用するコードを書くと、コンパイラによって、具体的な型に置き換えられたコードが生成されます。
// 以下は、T にそれぞれ int、double を指定している
<int> intStore(100);
DataStore<double> doubleStore(123.45);
DataStore
// 推論できる (C++17以降)
(100);
DataStore intStore(123.45); DataStore doubleStore
使用時に記述する具体的な型名のほうをテンプレート実引数と呼びます。上記のコードのように明示的に指定できるほか、C++17 以降では、コンストラクタの仮引数とテンプレート仮引数が同じ場合には、コンストラクタに与えた実引数からの型推論が働くようになっています。
こうしたコードから、テンプレート仮引数 T が int になったものと、double になったものがそれぞれ必要とされていることが分かるので、コンパイラはそれぞれのコードを生成します。
// T に int を当てはめたバージョン
class DataStore {
public:
(int data) : mData(data)
DataStore{
}
inline int Get() const
{
return mData;
}
private:
int mData;
};
// T に double を当てはめたバージョン
class DataStore {
public:
(double data) : mData(data)
DataStore{
}
inline double Get() const
{
return mData;
}
private:
double mData;
};
このように、型が抽象化されたテンプレートから、型を具体化したコードを作り出すことを、テンプレートの実体化と呼びます。また、実体化されたコードのことを指して、テンプレートの特殊化(特殊化バージョン)と呼びます。テンプレートはその名の通り、実際のコードを生成するための雛形に過ぎません。
特定のテンプレート実引数に対してだけ、異なるコードを使わせたいという場合に、テンプレートの明示的特殊化をおこなうことができます。次のコードは、テンプレート実引数が MyData型の場合にだけ異なるコードを使用します。コードが異なって良いということに注意してください。
// プライマリテンプレート
template <typename T>
class DataStore {
public:
(T data) : mData(data)
DataStore{
}
inline T Get() const
{
return mData;
}
private:
;
T mData};
// MyData型に明示的特殊化したクラステンプレート
template <> // テンプレート仮引数はなくなったが、元がテンプレートであることを示すために必要
class DataStore<MyData> { // MyData型の明示的特殊化である
public:
(MyData data) : mData(data)
DataStore{
}
// メンバの内容が変わって構わない
inline MyData::value_type Get() const
{
return mData.value;
}
private:
;
MyData mData};
明示的特殊化されたバージョンに対して、元になっている側のテンプレートのことを、プライマリテンプレートと呼びます。
明示的特殊化では、テンプレート仮引数に具体的な指定が与えられるため、テンプレート仮引数は
template <>
のように空になっています。
一方、部分特殊化という機能もあり、こちらはテンプレート仮引数の指定を残しつつ、特定の場合のコードを定義できます。たとえば、T
がポインタ型の場合(DataStore<int*>
とか DataStore<double*>
のような使い方の場合)にだけ専用のコードにする、テンプレート仮引数が2つ以上あるときに、そのうち1つだけを
int型などの具体的な型にするといったことが可能です。
// T がポインタ型の場合に部分特殊化したクラステンプレート
template <typename T> // T は引き続き使用する
class DataStore<T*> { // T がポインタ型の場合の部分特殊化である
public:
(T* data) : mData(data)
DataStore{
}
inline const T* Get() const
{
return mData;
}
inline T* Get()
{
return mData;
}
private:
* mData;
T};
int x = 100;
<int> ds1(100); // プライマリテンプレートの方を実体化
DataStore<int*> ds2(&x); // 部分特殊化の方を実体化 DataStore
// プライマリテンプレート
template <typename T1, typename T2>
class MyPair {
// ...
};
// T2 が std::string の場合の部分特殊化
template <typename T1> // T1 は引き続き使用する
class MyPair<T1, std::string> { // T2 が std::string のときの部分特殊化である
// ...
};
<int, double> pair1; // プライマリテンプレートの方を実体化
MyPair<int, std::string> pair2; // 部分特殊化の方を実体化 MyPair
C++ のクラステンプレートについては C++編【言語解説】第20章で、明示的特殊化や部分特殊化については、第23章で解説しています。
Programming Place Plus のトップページへ
はてなブックマーク に保存 | Pocket に保存 | Facebook でシェア |
X で ポスト/フォロー | LINE で送る | noteで書く |
RSS | 管理者情報 | プライバシーポリシー |