部分模板特化
允许为给定类别的模板参数定制类和变量(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,它对数组类型有一个部分特化。
[编辑] 实参列表
以下限制适用于部分模板特化的实参列表
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 起) |
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
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 起)模板,并且有部分特化可用时,编译器必须决定是使用主模板还是其部分特化之一。
// 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 | 缺少对变量模板的引用 | 添加对变量模板的支持 |