一、複数パラメータを持つクラステンプレート
1. クラステンプレートは任意の複数の異なる型パラメータを定義できる
2. クラステンプレートの特殊化
(1)クラステンプレートの特定の実装を指定する
(2)一部のパラメータは明示的に指定する必要がある(例:class Container<T, T>ではTを明示指定)
(3)型パラメータごとに異なる実装を提供する(同じテンプレートを必要に応じて異なる方法で実装)
(4)クラステンプレートの特殊化の種類
- 部分特殊化:特定のルールで型パラメータを制約(型パラメータが残存)
- 完全特殊化:すべてのパラメータを明示的に指定
(5)特殊化に関する注意点
- 特殊化はテンプレートの別実装に過ぎない(提供する機能は同一、本質的には同じテンプレート)
- 特殊化クラス模板の使用方法は統一されている(オブジェクト定義時に全てのパラメータを明示的に指定)
#include <iostream>
#include <string>
using namespace std;
template<typename A, typename B>
class Container
{
public:
void combine(A x, B y)
{
cout << "void combine(A x, B y)" << endl;
cout << x + y << endl;
}
};
// ポインタ向け部分特殊化
template<typename A, typename B>
class Container<A*, B*>
{
public:
void combine(A* x, B* y)
{
cout << "void combine(A* x, B* y)" << endl;
cout << *x + *y << endl;
}
};
// 同型パラメータ向け部分特殊化
template<typename T>
class Container<T, T>
{
public:
void combine(T x, T y)
{
cout << "void combine(T x, T y)" << endl;
cout << x + y << endl;
}
void showType()
{
cout << "Container<T, T> が選択されました" << endl;
}
};
// 完全特殊化
template<>
class Container<void*, void*>
{
public:
void combine(void* x, void* y)
{
cout << "void combine(void* x, void* y)" << endl;
cout << "void* 型の演算はサポートされていません" << endl;
}
};
int main()
{
Container<int, float> obj1;
Container<long, long> obj2;
Container<void*, void*> obj3;
obj1.combine(1, 2.5);
obj2.combine(5, 5);
obj2.showType();
obj3.combine(nullptr, nullptr);
Container<int*, double*> obj4;
int a = 1;
double b = 0.1;
obj4.combine(&a, &b);
return 0;
}
二、特殊化と再定義の違い
1. 再定義
(1)元のクラステンプレートと新規クラス(または別のクラステンプレート)
(2)使用時に選択方法を検討する必要がある
2. 特殊化
(1)統一された 방법으로クラステンプレートと特殊化クラスを使用可能
(2)コンパイラは自動的に特殊化を優先的に選択
(3)関数テンプレートの特殊化(完全特殊化のみ可能)
3. 実装上の推奨事項
(1)関数テンプレートをオーバーロードする必要がある場合は、特殊化を優先的に検討
(2)特殊화로対応できない場合のみ、関数テンプレートによる再定義を検討
#include <iostream>
#include <string>
using namespace std;
template<typename A, typename B>
class Container
{
public:
void combine(A x, B y)
{
cout << "void combine(A x, B y)" << endl;
cout << x + y << endl;
}
};
template<typename A, typename B>
class Container<A*, B*>
{
public:
void combine(A* x, B* y)
{
cout << "void combine(A* x, B* y)" << endl;
cout << *x + *y << endl;
}
};
template<typename T>
class Container<T, T>
{
public:
void combine(T x, T y)
{
cout << "void combine(T x, T y)" << endl;
cout << x + y << endl;
}
void showType()
{
cout << "Container<T, T> が選択されました" << endl;
}
};
// クラステンプレートの再定義(新規クラスとして実装)
class ContainerVoidPtr
{
public:
void combine(void* x, void* y)
{
cout << "void combine(void* x, void* y)" << endl;
cout << "void* 型の演算はサポートされていません" << endl;
}
};
/************************ 関数テンプレート *****************************/
template<typename T>
bool Compare(T a, T b)
{
cout << "bool Compare(T a, T b)" << endl;
return a == b;
}
// 関数テンプレートの完全特殊化
template<>
bool Compare(double a, double b)
{
cout << "bool Compare(double a, double b)" << endl;
const double epsilon = 0.00000000000001;
double diff = a - b;
return (-epsilon < diff) && (diff < epsilon);
}
// 関数再定義(オーバーロード)
bool Compare(double a, double b)
{
const double epsilon = 0.00000000000001;
double diff = a - b;
return (-epsilon < diff) && (diff < epsilon);
}
int main()
{
ContainerVoidPtr obj3;
obj3.combine(nullptr, nullptr);
cout << endl;
cout << Compare<>(1.2, 0.6) << endl;
return 0;
}
三、まとめ
- クラステンプレートは任意の複数の異なる型パラメータを定義できる
- クラステンプレートは部分特殊化と完全特殊化をサポート
- 特殊化の本质はテンプレートの別実装
- 関数テンプレートは完全特殊化のみサポート
- 実装ではクラス(関数)再定義の代わりにテンプレート特殊化を使用することを推奨