名前空間
変種
操作

明示的な (完全な) テンプレートの特殊化

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++11)
その他
 
 
 
 

指定された一連のテンプレート引数に対して、テンプレートコードをカスタマイズできます。

目次

[編集] 構文

template <> 宣言

以下のいずれも完全に特殊化できます。

  1. 関数テンプレート
  2. クラステンプレート (class template)
  3. 変数テンプレート(C++14以降)
  4. クラステンプレートのメンバー関数
  5. クラステンプレートの静的データメンバー
  6. クラステンプレートのメンバークラス
  7. クラステンプレートのメンバー列挙型
  8. クラスまたはクラステンプレートのメンバークラステンプレート
  9. クラスまたはクラステンプレートのメンバー関数テンプレート
  10. クラスまたはクラステンプレートのメンバー変数テンプレート(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>&);

特殊化と同じ名前と同じ引数リストを持つ関数は特殊化ではありません (関数テンプレートのテンプレートオーバーロードを参照)。

クラスが暗黙的にインスタンス化される場合、関数テンプレート、メンバー関数テンプレート、およびクラステンプレートのメンバー関数の明示的な特殊化では、デフォルト関数引数を指定できません。

明示的な特殊化はフレンド宣言にはなれません。

[編集] 特殊化のメンバー

明示的に特殊化されたクラステンプレートのメンバーをクラスの本体の外で定義する場合、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 プライマリテンプレートのconstinitconsteval
その明示的な特殊化に引き継がれるかどうかは不明でした
引き継がれない
CWG 2604 C++11 プライマリテンプレートの属性が
その明示的な特殊化に引き継がれるかどうかは不明でした
引き継がれない

[編集] 関連項目

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