名稱空間
變體
操作

部分模板特化

來自 cppreference.com
< cpp‎ | 語言
 
 
C++ 語言
表示式
替代表示
字面量
布林字面量 - 整數字面量 - 浮點字面量
字元字面量 - 字串字面量 - nullptr (C++11)
使用者定義 (C++11)
工具
屬性 (C++11)
型別
typedef 宣告
類型別名宣告 (C++11)
型別轉換
記憶體分配
類特有的函式屬性
explicit (C++11)
static

特殊成員函式
模板
雜項
 
 
 
 

允許為給定類別的模板引數定製類和變數(C++14 起)模板。

目錄

[編輯] 語法

template < 引數列表 > 類鍵 類頭名 < 實參列表 > 宣告 (1)
template < 引數列表 > 宣告符序列 宣告符 < 實參列表 > 初始化器 (可選) (2) (C++14 起)

其中 類頭名 標識先前宣告的類模板的名稱宣告符 標識先前宣告的變數模板的名稱(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,它對陣列型別有一個部分特化。

[編輯] 實參列表

以下限制適用於部分模板特化的實參列表

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) 如果只有一個特化與模板實參匹配,則使用該特化
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 接受的型別集合的子集”。

形式上,為了建立部分特化之間的“更特化”關係,每個特化首先按如下方式轉換為一個虛構的函式模板

  • 第一個函式模板具有與第一個部分特化相同的模板引數,並且只有一個函式引數,其型別是類模板特化,包含第一個部分特化的所有模板實參
  • 第二個函式模板具有與第二個部分特化相同的模板引數,並且只有一個函式引數,其型別是類模板特化,包含第二個部分特化的所有模板實參。

然後像函式模板過載一樣對函式模板進行排名。

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++ 標準。

缺陷報告 應用於 釋出時的行為 正確的行為
CWG 727 C++98 不允許在類作用域中進行部分和完全特化
類作用域
允許在任何作用域中
CWG 1315 C++98 模板引數不能在非型別模板實參中使用,除了 id-expressions
模板實參,除了 id-expressions
只要可推導,表示式即可
CWG 1495 C++11 涉及引數包時規範不明確 特化應更特化
CWG 1711 C++14 缺少變數模板部分特化的規範 新增對變數模板的支援
CWG 1819 C++98 部分特化定義的允許作用域 使部分特化可以與主模板在同一作用域中宣告
與主模板在同一作用域中
CWG 2330 C++14 缺少對變數模板的引用 新增對變數模板的支援

[編輯] 參閱