テンプレートの明示的 (完全) 特殊化
テンプレートのコードを特定のテンプレート引数の集合に対してカスタマイズすることを可能にします。
目次 |
[編集]構文
template <> declaration | |||||||||
以下のものは完全特殊化できます。
- 関数テンプレート。
- クラステンプレート。
- (C++14以上)変数テンプレート。
- クラステンプレートのメンバ関数。
- クラステンプレートの静的データメンバ。
- クラステンプレートのメンバクラス。
- クラステンプレートのメンバ列挙。
- クラスまたはクラステンプレートのメンバクラステンプレート。
- クラスまたはクラステンプレートのメンバ関数テンプレート。
例えば、
#include <iostream>template<typename T>// プライマリテンプレート。struct is_void :std::false_type{};template<>// T = void に対する明示的特殊化。struct is_void<void>:std::true_type{};int main(){// void 以外の任意の T に対して、// このクラスは false_type から派生します。std::cout<< is_void<char>::value<<'\n';// しかし T が void のときは、// このクラスは true_type から派生します。std::cout<< is_void<void>::value<<'\n';}
[編集]詳細
明示的特殊化は、そのプライマリテンプレートを定義してよい任意のスコープで宣言できます (これはプライマリテンプレートが定義されているスコープと異なることがあります (メンバテンプレートのクラス外における特殊化の場合など)) 。 明示的特殊化は特殊化されていないテンプレート宣言の後に現れなければなりません。
namespace N {template<class T>class X {/*...*/};// プライマリテンプレート。template<>class X<int>{/*...*/};// 同じ名前空間での特殊化。 template<class T>class Y {/*...*/};// プライマリテンプレート。template<>class Y<double>;// double に対する特殊化の前方宣言。}template<>class N::Y<double>{/*...*/};// OK、同じ名前空間での特殊化。
特殊化は、暗黙の実体化を発生させるであろう最初の使用よりも前に、そのような使用が発生するすべての翻訳単位内で、宣言されなければなりません。
class String {};template<class T>class Array {/*...*/};template<class T>void sort(Array<T>& v){/*...*/}// プライマリテンプレート。 void f(Array<String>& v){ sort(v);// sort() のプライマリテンプレートを用いて// sort(Array<String>&) を暗黙に実体化します。 template<>// エラー、 sort(Array<String>&) を暗黙に実体化した後の明示的特殊化。void sort<String>(Array<String>& v);
宣言されているけれども定義されていないテンプレートの特殊化は、他のあらゆる不完全型とまったく同様に使用できます (例えばその型へのポインタや参照を使用しても構いません)。
template<class T>class X;// プライマリテンプレート。template<>class X<int>;// 特殊化 (宣言されているけれども定義されていません)。 X<int>* p;// OK、不完全型へのポインタ。 X<int> x;// エラー、不完全型のオブジェクト。
[編集]関数テンプレートの明示的特殊化
関数テンプレートを特殊化するとき、テンプレートの実引数推定によって関数引数から推定できる場合は、テンプレート引数を省略できます。
template<class T>class Array {/*...*/};template<class T>void sort(Array<T>& v);// プライマリテンプレート。template<>void sort(Array<int>&);// T = int に対する特殊化。// template<> void sort<int>(Array<int>&); と書く必要はありません。
特殊化と同じ名前と同じ引数リストを持つ関数は特殊化ではありません (関数テンプレートのテンプレートオーバーロードを参照してください)。
関数テンプレートの明示的特殊化は、それが inline 指定子付きで宣言されている (または削除されたものとして定義されている) 場合にのみインラインであり、プライマリテンプレートがインラインかどうかは影響しません。
デフォルト関数引数は、関数テンプレート、メンバ関数テンプレート、およびクラスが暗黙に実体化されるときのクラステンプレートのメンバ関数の明示的特殊化では、指定できません。
明示的特殊化はフレンド宣言にできません。
プライマリテンプレートが noexcept(false)
でない例外指定を持つ場合、その明示的特殊化は互換性のある例外指定を持たなければなりません。
[編集]特殊化のメンバ
クラス本体の外側で明示的に特殊化されたクラステンプレートのメンバを定義するとき、構文 template<> は使用されません。 ただしそれがクラステンプレートとして特殊化された明示的に特殊化されたメンバクラステンプレートのメンバである場合を除きます (そうでなければそのような定義はネストしたテンプレートによって要求される template<引数> で始まることが構文的に要求されるであろうためです)。
template<typename T>struct A {struct B {};// メンバクラス。template<class U>struct C {};// メンバクラステンプレート。}; template<>// 特殊化。struct A<int>{void f(int);// 特殊化のメンバ関数。};// 特殊化のメンバに対しては template<> を使用しません。void A<int>::f(int){/* ... */} template<>// メンバクラスの特殊化。struct A<char>::B{void f();};// 特殊化されたメンバクラスのメンバに対しても template<> を使用しません。void A<char>::B::f(){/* ... */} template<>// メンバクラステンプレートの特殊化。template<class U>struct A<char>::C{void f();}; // クラステンプレートとして特殊化された// 明示的に特殊化されたメンバクラステンプレートのメンバを定義するときは// template<> を使用します。template<>template<class U>void A<char>::C<U>::f(){/* ... */}
テンプレートの静的データメンバの明示的特殊化は、その宣言が初期化子を含む場合は定義です。 そうでなければ、それは宣言です。 デフォルト初期化する場合は波括弧を使用しなければなりません。
template<> X Q<int>::x;// 静的メンバの宣言。template<> X Q<int>::x();// エラー、関数の宣言。template<> X Q<int>::x{};// デフォルト初期化された静的メンバの定義。
クラステンプレートのメンバまたはメンバテンプレートは、たとえそのメンバまたはメンバテンプレートがクラステンプレート定義内で定義されていても、特定のクラステンプレートの暗黙に実体化のために明示的特殊化しても構いません。
template<typename T>struct A {void f(T);// メンバ (プライマリテンプレート内で宣言されている)。void h(T){}// メンバ (プライマリテンプレート内で定義されている)。template<class X1>void g1(T, X1);// メンバテンプレート。template<class X2>void g2(T, X2);// メンバテンプレート。}; // メンバの特殊化。template<>void A<int>::f(int);// クラス内で定義されていてもメンバを特殊化できます。template<>void A<int>::h(int){} // クラスの外側におけるメンバテンプレートの定義。template<class T>template<class X1>void A<T>::g1(T, X1){} // メンバテンプレートの特殊化。template<>template<class X1>void A<int>::g1(int, X1); // メンバテンプレートの特殊化。template<>template<>void A<int>::g2<char>(int, char);// X2 = char に対して。// 同上 (テンプレートの実引数推定 (X1 = char) を使用しています)。template<>template<>void A<int>::g1(int, char);
メンバまたはメンバテンプレートは多数の囲っているクラステンプレート内にネストしても構いません。 そのようなメンバに対する明示的特殊化では、明示的に特殊化されるすべての囲っているクラステンプレートに対して template<> が存在します。
template<class T1>class A {template<class T2>class B {void mf();};};template<>template<>class A<int>::B<double>;template<>template<>void A<char>::B<char>::mf();
そのようなネストした宣言では、一部の段を特殊化しなくても構いません (ただし、囲っているクラスが特殊化されていない場合、クラスメンバテンプレートを特殊化することはできません)。 そのような特殊化はそれ自身がテンプレートであるため、そのような段のそれぞれについて、 template<引数> が必要です。
template<class T1>class A {template<class T2>class B {template<class T3>void mf1(T3);// メンバテンプレート。void mf2();// 非テンプレートメンバ。};}; // 特殊化。template<>// 特殊化される A のため。template<class X>// 特殊化されない B のため。class A<int>::B{template<class T>void mf1(T);}; // 特殊化。template<>// 特殊化される A のため。template<>// 特殊化される B のため。template<class T>// 特殊化されない mf1 のため。void A<int>::B<double>::mf1(T t){} // エラー、 B<double> は特殊化されるメンバテンプレートであるため、// その囲っている A も特殊化されなければなりません。template<class Y>template<>void A<Y>::B<double>::mf2(){}
[編集]欠陥報告
以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。
DR | 適用先 | 発行時の動作 | 正しい動作 |
---|---|---|---|
CWG 727 | C++14 | full specializations not allowed in class scope, even though partial are | full specialization allowed in any scope |