名前空間
変種
操作

部分テンプレート特殊化

From cppreference.com
< cpp‎ | language
 
 
C++言語
全般
フロー制御
条件実行文
if
繰り返し文 (ループ)
for
範囲for (C++11)
ジャンプ文
関数
関数宣言
ラムダ式
inline指定子
動的例外仕様 (C++17まで*)
noexcept指定子 (C++11)
例外
名前空間
指定子
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点数
文字 - 文字列 - nullptr (C++11)
ユーザー定義 (C++11)
ユーティリティ
属性 (C++11)
typedef宣言
型エイリアス宣言 (C++11)
キャスト
メモリ確保
クラス
クラス固有の関数プロパティ
explicit (C++11)
static

特殊メンバ関数
テンプレート
その他
 
 
 
 

特定のカテゴリのテンプレート引数に対して、クラスと変数(C++14以降)テンプレートをカスタマイズできます。

目次

[編集] 構文

template < parameter-list > class-key class-head-name < argument-list > declaration (1)
template < parameter-list > decl-specifier-seq declarator < argument-list > initializer (任意) (2) (C++14以降)

ここで、class-head-name は以前に宣言されたクラステンプレートの名前を識別しdeclarator は以前に宣言された変数テンプレートの名前を識別します(C++14以降)

部分特殊化は、そのプライマリテンプレートが定義できる任意のスコープで宣言できます(これはプライマリテンプレートが定義されるスコープとは異なる場合があります。例えば、メンバテンプレートのクラス外特殊化の場合など)。部分特殊化は、非特殊化テンプレート宣言の後に記述する必要があります。

例えば、

template<class T1, class T2, int I>
class A {};             // primary template
 
template<class T, int I>
class A<T, T*, I> {};   // #1: partial specialization where T2 is a pointer to T1
 
template<class T, class T2, int I>
class A<T*, T2, I> {};  // #2: partial specialization where T1 is a pointer
 
template<class T>
class A<int, T*, 5> {}; // #3: partial specialization where
                        //     T1 is int, I is 5, and T2 is a pointer
 
template<class X, class T, int I>
class A<X, T*, I> {};   // #4: partial specialization where T2 is a pointer

標準ライブラリにおける部分特殊化の例には、配列型に対する部分特殊化を持つ std::unique_ptr が含まれます。

[編集] 引数リスト

部分テンプレート特殊化のargument-listには以下の制約が適用されます。

1) 引数リストは、非特殊化された引数リストと同一であってはならない(何らかの特殊化が必要)
template<class T1, class T2, int I> class B {};        // primary template
template<class X, class Y, int N> class B<X, Y, N> {}; // error

さらに、特殊化はプライマリテンプレートよりも特殊化されている必要があります。

template<int N, typename T1, typename... Ts> struct B;
template<typename... Ts> struct B<0, Ts...> {}; // Error: not more specialized
(C++11以降)
2) デフォルト引数は引数リストに含めることはできません
3) いずれかの引数がパック展開である場合、それはリストの最後の引数でなければなりません
4) 非型引数式は、テンプレートパラメータが非推論コンテキストの外に少なくとも一度出現する限り、テンプレートパラメータを使用できます(現在、clangとgcc 12のみがこの機能をサポートしていることに注意してください)
template<int I, int J> struct A {};
template<int I> struct A<I + 5, I * 2> {}; // error, I is not deducible
 
template<int I, int J, int K> struct B {};
template<int I> struct B<I, I * 2, 2> {};  // OK: first parameter is deducible
5) 非型テンプレート引数は、その型が特殊化のパラメータに依存するテンプレートパラメータを特殊化することはできません
template<class T, T t> struct C {}; // primary template
template<class T> struct C<T, 1>;   // error: type of the argument 1 is T,
                                    // which depends on the parameter T
 
template<int X, int (*array_ptr)[X]> class B {}; // primary template
int array[5];
template<int X> class B<X, &array> {}; // error: type of the argument &array is
                                       // int(*)[X], which depends on the parameter X

[編集] 名前探索

部分テンプレート特殊化は名前探索によって見つかりません。プライマリテンプレートが名前探索によって見つかった場合にのみ、その部分特殊化が考慮されます。特に、プライマリテンプレートを可視にするusing宣言は、部分特殊化も可視にします。

namespace N
{
    template<class T1, class T2> class Z {}; // primary template
}
using N::Z; // refers to the primary template
 
namespace N
{
    template<class T> class Z<T, T*> {};     // partial specialization
}
Z<int, int*> z; // name lookup finds N::Z (the primary template), the
                // partial specialization with T = int is then used

[編集] 部分順序付け

クラスまたは変数(C++14以降)テンプレートがインスタンス化され、部分特殊化が利用可能な場合、コンパイラはプライマリテンプレートを使用するか、その部分特殊化のいずれかを使用するかを決定する必要があります。

1) テンプレート引数に一致する特殊化が1つだけの場合、その特殊化が使用されます
2) 複数の特殊化が一致する場合、部分順序付けルールが使用され、どの特殊化がより特殊化されているかが決定されます。最も特殊化された特殊化がユニークな場合に使用されます(ユニークでない場合、プログラムはコンパイルできません)
3) 一致する特殊化がない場合、プライマリテンプレートが使用されます
// given the template A as defined above
A<int, int, 1> a1;   // no specializations match, uses primary template
A<int, int*, 1> a2;  // uses partial specialization #1 (T = int, I = 1)
A<int, char*, 5> a3; // uses partial specialization #3, (T = char)
A<int, char*, 1> a4; // uses partial specialization #4, (X = int, T = char, I = 1)
A<int*, int*, 2> a5; // error: matches #2 (T = int, T2 = int*, I= 2)
                     //        matches #4 (X = int*, T = int, I = 2)
                     // neither one is more specialized than the other

非公式には、「AはBよりも特殊化されている」とは、「AはBが受け入れる型のサブセットを受け入れる」という意味です。

正式には、部分特殊化間のより特殊化された関係を確立するために、それぞれが最初に次のように架空の関数テンプレートに変換されます。

  • 最初の関数テンプレートは、最初の部分特殊化と同じテンプレートパラメータを持ち、1つの関数パラメータのみを持ち、その型は最初の部分特殊化のすべてのテンプレート引数を持つクラステンプレート特殊化です。
  • 2番目の関数テンプレートは、2番目の部分特殊化と同じテンプレートパラメータを持ち、1つの関数パラメータのみを持ち、その型は2番目の部分特殊化のすべてのテンプレート引数を持つクラステンプレート特殊化です。

関数テンプレートは、関数テンプレートのオーバーロードの場合と同様にランク付けされます。

template<int I, int J, class T> struct X {}; // primary template
template<int I, int J>          struct X<I, J, int>
{
    static const int s = 1;
}; // partial specialization #1
// fictitious function template for #1 is
// template<int I, int J> void f(X<I, J, int>); #A
 
template<int I>                 struct X<I, I, int>
{
    static const int s = 2;
}; // partial specialization #2
// fictitious function template for #2 is 
// template<int I>        void f(X<I, I, int>); #B
 
int main()
{
    X<2, 2, int> x; // both #1 and #2 match
// partial ordering for function templates:
// #A from #B: void(X<I, J, int>) from void(X<U1, U1, int>): deduction OK
// #B from #A: void(X<I, I, int>) from void(X<U1, U2, int>): deduction fails
// #B is more specialized
// #2 is the specialization that is instantiated
    std::cout << x.s << '\n'; // prints 2
}

[編集] 部分特殊化のメンバ

部分特殊化のメンバのテンプレートパラメータリストとテンプレート引数リストは、部分特殊化のパラメータリストと引数リストに一致しなければなりません。

プライマリテンプレートのメンバと同様に、プログラムで使用された場合にのみ定義する必要があります。

部分特殊化のメンバは、プライマリテンプレートのメンバとは関連付けられていません。

部分特殊化のメンバの明示的(完全)特殊化は、プライマリテンプレートの明示的特殊化と同じ方法で宣言されます。

template<class T, int I> // primary template
struct A
{
    void f(); // member declaration
};
 
template<class T, int I>
void A<T, I>::f() {}     // primary template member definition
 
// partial specialization
template<class T>
struct A<T, 2>
{
    void f();
    void g();
    void h();
};
 
// member of partial specialization
template<class T>
void A<T, 2>::g() {}
 
// explicit (full) specialization
// of a member of partial specialization
template<>
void A<char, 2>::h() {}
 
int main()
{
    A<char, 0> a0;
    A<char, 2> a2;
    a0.f(); // OK, uses primary template’s member definition
    a2.g(); // OK, uses partial specialization's member definition
    a2.h(); // OK, uses fully-specialized definition of
            // the member of a partial specialization
    a2.f(); // error: no definition of f() in the partial
            // specialization A<T,2> (the primary template is not used)
}

プライマリテンプレートが別のクラステンプレートのメンバである場合、その部分特殊化は囲むクラステンプレートのメンバです。囲むテンプレートがインスタンス化されると、各メンバの部分特殊化の宣言もインスタンス化されます(テンプレートの他のすべてのメンバの宣言はインスタンス化されますが、定義はインスタンス化されないのと同じ方法です)。

プライマリメンバテンプレートが、囲むクラステンプレートの特定の(暗黙的)特殊化に対して明示的に(完全に)特殊化されている場合、そのメンバテンプレートの部分特殊化は、囲むクラステンプレートのこの特殊化に対して無視されます。

メンバテンプレートの部分特殊化が、囲むクラステンプレートの特定の(暗黙的)特殊化に対して明示的に特殊化されている場合、プライマリメンバテンプレートとその他の部分特殊化は、囲むクラステンプレートのこの特殊化に対して引き続き考慮されます。

template<class T> struct A // enclosing class template
{
    template<class T2>
    struct B {};      // primary member template
    template<class T2>
    struct B<T2*> {}; // partial specialization of member template
};
 
template<>
template<class T2>
struct A<short>::B {}; // full specialization of primary member template
                       // (will ignore the partial)
 
A<char>::B<int*> abcip;  // uses partial specialization T2=int
A<short>::B<int*> absip; // uses full specialization of the primary (ignores partial)
A<char>::B<int> abci;    // uses primary

[編集] 欠陥報告

以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。

DR 適用対象 公開された動作 正しい動作
CWG 727 C++98 部分特殊化と完全特殊化は許可されない
クラススコープ
任意のスコープで許可される
CWG 1315 C++98 テンプレートパラメータは非型で使用できなかった
id-expression以外のテンプレート引数
推論可能であれば式はOK
CWG 1495 C++11 パラメータパックを含む場合の仕様が不明確だった 特殊化はより特殊化されている必要がある
CWG 1711 C++14 変数テンプレートの部分特殊化の仕様が不足 変数テンプレートのサポートを追加
CWG 1819 C++98 部分特殊化の定義に許容されるスコープ 部分特殊化を宣言できるようにする
プライマリテンプレートと同じスコープで
CWG 2330 C++14 変数テンプレートへの参照が不足 変数テンプレートのサポートを追加

[編集] 関連項目

English 日本語 中文(简体) 中文(繁體)