名稱空間
變體
操作

顯式(完全)模板特化

來自 cppreference.com
< cpp‎ | 語言
 
 
C++ 語言
 
 
 
 

允許為給定模板引數集定製模板程式碼。

目錄

[編輯] 語法

template <> 宣告

以下任何項都可以完全特化:

  1. 函式模板
  2. 類模板
  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++ 標準。

缺陷報告 應用於 釋出時的行為 正確的行為
CWG 531 C++98 未指定在名稱空間作用域中定義顯式特化成員的語法
未指定在名稱空間作用域中定義顯式
已指定
CWG 727 C++98 類作用域中不允許部分和完全特化
在任何作用域中都允許
允許在任何作用域中
CWG 730 C++98 非模板類的成員模板不能完全特化
類無法完全特化
允許
CWG 2478 C++20 不清楚主模板的constinitconsteval是否會傳遞到其顯式特化中
不清楚主模板的constinitconsteval是否
未傳遞
CWG 2604 C++11 不清楚主模板的屬性是否會傳遞到其顯式特化中
模板的屬性是否會傳遞到其顯式特化中
未傳遞

[編輯] 另請參閱