明示的な (完全な) テンプレートの特殊化
指定された一連のテンプレート引数に対して、テンプレートコードをカスタマイズできます。
目次 |
[編集] 構文
template <> 宣言 |
|||||||||
以下のいずれも完全に特殊化できます。
- 関数テンプレート
- クラステンプレート (class template)
- 変数テンプレート(C++14以降)
- クラステンプレートのメンバー関数
- クラステンプレートの静的データメンバー
- クラステンプレートのメンバークラス
- クラステンプレートのメンバー列挙型
- クラスまたはクラステンプレートのメンバークラステンプレート
- クラスまたはクラステンプレートのメンバー関数テンプレート
- クラスまたはクラステンプレートのメンバー変数テンプレート(C++14以降)
例えば、
#include <type_traits> template<typename T> // primary template struct is_void : std::false_type {}; template<> // explicit specialization for T = void struct is_void<void> : std::true_type {}; int main() { static_assert(is_void<char>::value == false, "for any type T other than void, the class is derived from false_type"); static_assert(is_void<void>::value == true, "but when T is void, the class is derived from true_type"); }
[編集] 詳細
明示的な特殊化は、プライマリテンプレートが定義される可能性のある任意のスコープで宣言できます (プライマリテンプレートが定義されるスコープとは異なる場合もあります。例えば、メンバーテンプレートのクラス外特殊化の場合など)。明示的な特殊化は、非特殊化テンプレート宣言の後に記述する必要があります。
namespace N { template<class T> // primary template class X { /*...*/ }; template<> // specialization in same namespace class X<int> { /*...*/ }; template<class T> // primary template class Y { /*...*/ }; template<> // forward declare specialization for double class Y<double>; } template<> // OK: specialization in same namespace class N::Y<double> { /*...*/ };
特殊化は、暗黙的なインスタンス化を引き起こす最初の使用の前に、そのような使用が発生するすべての翻訳単位で宣言する必要があります。
class String {}; template<class T> class Array { /*...*/ }; template<class T> // primary template void sort(Array<T>& v) { /*...*/ } void f(Array<String>& v) { sort(v); // implicitly instantiates sort(Array<String>&), } // using the primary template for sort() template<> // ERROR: explicit specialization of sort(Array<String>) void sort<String>(Array<String>& v); // after implicit instantiation
宣言されたが定義されていないテンプレートの特殊化は、他の不完全型と同様に使用できます (例えば、それへのポインターや参照が使用できます)。
template<class T> // primary template class X; template<> // specialization (declared, not defined) class X<int>; X<int>* p; // OK: pointer to incomplete type X<int> x; // error: object of incomplete type
関数または変数(C++14以降)テンプレートの明示的な特殊化がinline/constexpr(C++11以降)/constinit/consteval(C++20以降)であるかどうかは、プライマリテンプレートがその指定子で宣言されているかどうかにかかわらず、明示的な特殊化自体によって決定されます。同様に、テンプレートの宣言に現れる属性は、そのテンプレートの明示的な特殊化には影響しません。(C++11以降)
template<class T> void f(T) { /* ... */ } template<> inline void f<>(int) { /* ... */ } // OK, inline template<class T> inline T g(T) { /* ... */ } template<> int g<>(int) { /* ... */ } // OK, not inline template<typename> [[noreturn]] void h([[maybe_unused]] int i); template<> void h<int>(int i) { // [[noreturn]] has no effect, but [[maybe_unused]] has }
[編集] 関数テンプレートの明示的な特殊化
関数テンプレートを特殊化する場合、テンプレート引数推論によって関数引数からそれらを提供できる場合は、テンプレート引数を省略できます。
template<class T> class Array { /*...*/ }; template<class T> // primary template void sort(Array<T>& v); template<> // specialization for T = int void sort(Array<int>&); // no need to write // template<> void sort<int>(Array<int>&);
特殊化と同じ名前と同じ引数リストを持つ関数は特殊化ではありません (関数テンプレートのテンプレートオーバーロードを参照)。
クラスが暗黙的にインスタンス化される場合、関数テンプレート、メンバー関数テンプレート、およびクラステンプレートのメンバー関数の明示的な特殊化では、デフォルト関数引数を指定できません。
明示的な特殊化はフレンド宣言にはなれません。
| このセクションは未完成です 理由: さまざまなC++バージョンにおける例外指定の要件を確認する |
[編集] 特殊化のメンバー
明示的に特殊化されたクラステンプレートのメンバーをクラスの本体の外で定義する場合、template<>という構文は使用されません。ただし、それが明示的に特殊化されたメンバークラステンプレートのメンバーであり、クラステンプレートとして特殊化されている場合は例外です。そうでなければ、構文は、そのような定義がネストされたテンプレートによって必要とされるtemplate<parameters>で始まることを要求するからです。
template<typename T> struct A { struct B {}; // member class template<class U> // member class template struct C {}; }; template<> // specialization struct A<int> { void f(int); // member function of a specialization }; // template<> not used for a member of a specialization void A<int>::f(int) { /* ... */ } template<> // specialization of a member class struct A<char>::B { void f(); }; // template<> not used for a member of a specialized member class either void A<char>::B::f() { /* ... */ } template<> // specialization of a member class template template<class U> struct A<char>::C { void f(); }; // template<> is used when defining a member of an explicitly // specialized member class template specialized as a class template template<> template<class U> void A<char>::C<U>::f() { /* ... */ }
テンプレートの静的データメンバーの明示的な特殊化は、宣言に初期化子が含まれている場合は定義であり、それ以外の場合は宣言です。これらの定義では、デフォルト初期化に波括弧を使用する必要があります。
template<> X Q<int>::x; // declaration of a static member template<> X Q<int>::x (); // error: function declaration template<> X Q<int>::x {}; // definition of a default-initialized static member
クラステンプレートのメンバーまたはメンバーテンプレートは、クラステンプレートの特定の暗黙的なインスタンス化に対して明示的に特殊化できます。これは、メンバーまたはメンバーテンプレートがクラステンプレート定義内で定義されている場合でも可能です。
template<typename T> struct A { void f(T); // member, declared in the primary template void h(T) {} // member, defined in the primary template template<class X1> // member template void g1(T, X1); template<class X2> // member template void g2(T, X2); }; // specialization of a member template<> void A<int>::f(int); // member specialization OK even if defined in-class template<> void A<int>::h(int) {} // out of class member template definition template<class T> template<class X1> void A<T>::g1(T, X1) {} // member template specialization template<> template<class X1> void A<int>::g1(int, X1); // member template specialization template<> template<> void A<int>::g2<char>(int, char); // for X2 = char // same, using template argument deduction (X1 = char) template<> template<> void A<int>::g1(int, char);
メンバーまたはメンバーテンプレートは、多くの囲むクラステンプレート内にネストされている場合があります。そのようなメンバーの明示的な特殊化では、明示的に特殊化されるすべての囲むクラステンプレートに対してtemplate<>が存在します。
template<class T1> struct A { template<class T2> struct B { template<class T3> void mf(); }; }; template<> struct A<int>; template<> template<> struct A<char>::B<double>; template<> template<> template<> void A<char>::B<char>::mf<double>();
このようなネストされた宣言では、いくつかのレベルは特殊化されないままになることがあります (ただし、囲むクラスが特殊化されていない場合、名前空間スコープでクラスメンバーテンプレートを特殊化することはできません)。これらのレベルのそれぞれについて、宣言にはtemplate<arguments>が必要です。なぜなら、そのような特殊化自体がテンプレートだからです。
template<class T1> class A { template<class T2> class B { template<class T3> // member template void mf1(T3); void mf2(); // non-template member }; }; // specialization template<> // for the specialized A template<class X> // for the unspecialized B class A<int>::B { template<class T> void mf1(T); }; // specialization template<> // for the specialized A template<> // for the specialized B template<class T> // for the unspecialized mf1 void A<int>::B<double>::mf1(T t) {} // ERROR: B<double> is specialized and is a member template, so its enclosing A // must be specialized also template<class Y> template<> void A<Y>::B<double>::mf2() {}
[編集] 欠陥レポート
以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| CWG 531 | C++98 | 明示的なメンバーの定義構文 名前空間スコープでの特殊化は指定されていませんでした |
指定された |
| CWG 727 | C++98 | 部分および完全特殊化は許可されていません クラススコープ |
任意のスコープで許可 |
| CWG 730 | C++98 | 非テンプレートのメンバーテンプレート クラスは完全に特殊化できませんでした |
許可 |
| CWG 2478 | C++20 | プライマリテンプレートのconstinitとconstevalが その明示的な特殊化に引き継がれるかどうかは不明でした |
引き継がれない |
| CWG 2604 | C++11 | プライマリテンプレートの属性が その明示的な特殊化に引き継がれるかどうかは不明でした |
引き継がれない |