Explicit (full) template specialization
指定されたテンプレート引数のセットに対して、テンプレートコードをカスタマイズすることを可能にします。
目次 |
構文
template <>
宣言
|
|||||||||
以下のいずれかを完全特殊化することができます:
- function template
- class template
- variable template (C++14以降)
- member function クラステンプレートの
- static data member クラステンプレートの
- member class クラステンプレートの
- メンバー enumeration クラステンプレートの
- member class template クラスまたはクラステンプレートの
- member function template クラスまたはクラステンプレートの
- member variable template クラスまたはクラステンプレートの (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> // プライマリテンプレート class X { /*...*/ }; template<> // 同じ名前空間内の特殊化 class X<int> { /*...*/ }; template<class T> // プライマリテンプレート class Y { /*...*/ }; template<> // doubleの特殊化を前方宣言 class Y<double>; } template<> // OK: 同じ名前空間内の特殊化 class N::Y<double> { /*...*/ };
特殊化は、暗黙的なインスタンス化を引き起こす最初の使用の前に、そのような使用が発生するすべての翻訳単位で宣言されなければなりません:
class String {}; template<class T> class Array { /*...*/ }; template<class T> // プライマリテンプレート void sort(Array<T>& v) { /*...*/ } void f(Array<String>& v) { sort(v); // sort(Array<String>&) を暗黙的にインスタンス化、 } // sort() のプライマリテンプレートを使用 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; // エラー: 不完全型のオブジェクト
関数
または変数
(since C++14)
テンプレートの明示的特殊化が
inline
/
constexpr
(since C++11)
/
constinit
/
consteval
(since C++20)
であるかどうかは、明示的特殊化自体によって決定され、プライマリテンプレートがその指定子で宣言されているかどうかには依存しません。
同様に、テンプレートの宣言に現れる
属性
は、そのテンプレートの明示的特殊化には影響しません:
(since 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、inlineではない template<typename> [[noreturn]] void h([[maybe_unused]] int i); template<> void h<int>(int i) { // [[noreturn]]は効果を持たないが、[[maybe_unused]]は効果を持つ }
関数テンプレートの明示的特殊化
関数テンプレートを特殊化する際、関数引数から テンプレート引数推論 によってテンプレート引数を推定できる場合、テンプレート引数を省略することができます:
template<class T> class Array { /*...*/ }; template<class T> // プライマリテンプレート void sort(Array<T>& v); template<> // T = int に対する特殊化 void sort(Array<int>&); // 以下の記述は不要 // template<> void sort<int>(Array<int>&);
特殊化と同じ名前と同じ引数リストを持つ関数は特殊化ではありません( function template のテンプレートオーバーロードを参照)。
デフォルト関数引数 は、関数テンプレートの明示的特殊化、メンバ関数テンプレート、およびクラスが暗黙的にインスタンス化される場合のクラステンプレートのメンバ関数では指定できません。
明示的特殊化は friend宣言 になることはできません。
|
このセクションは不完全です
理由: 異なるC++バージョンにおける例外指定の要件を確認してください |
特殊化のメンバー
明示的に特殊化されたクラステンプレートのメンバーをクラス本体の外で定義する場合、 template <> という構文は使用されません。ただし、それが明示的に特殊化されたメンバークラステンプレートのメンバーであり、そのメンバークラステンプレート自体がクラステンプレートとして特殊化されている場合は除きます。なぜなら、そうでなければ、その定義はネストされたテンプレートに必要な template < parameters > で始まる必要があるためです。
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> 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>();
`タグ内のC++コードは翻訳せず、元のフォーマットを保持しています。このコードはテンプレートの特殊化とネストされたテンプレート構造を示すC++の例であり、翻訳対象となる自然言語のテキストは含まれていません。
このようなネストされた宣言では、一部のレベルが非特殊化のままである場合があります(ただし、外側のクラスが非特殊化の場合、名前空間スコープでクラスメンバーテンプレートを特殊化することはできません)。それらのレベルのそれぞれについて、宣言には template < arguments > が必要です。なぜなら、そのような特殊化自体がテンプレートであるためです:
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 531 | C++98 |
名前空間スコープでの明示的特殊化のメンバ定義構文が
規定されていなかった |
規定された |
| CWG 727 | C++98 |
部分特殊化と完全特殊化がクラススコープで
許可されていなかった |
任意のスコープで許可 |
| CWG 730 | C++98 |
非テンプレートクラスのメンバテンプレートを
完全特殊化できなかった |
許可された |
| CWG 2478 | C++20 |
プライマリテンプレートの
constinit
と
consteval
が
明示的特殊化に引き継がれるか不明確だった |
引き継がれない |
| CWG 2604 | C++11 |
プライマリテンプレートの属性が明示的特殊化に
引き継がれるか不明確だった |
引き継がれない |