命名空间
变体
操作

偏特化模板

来自 cppreference.com
< cpp‎ | 语言
 
 
C++ 语言
表达式
替代表示
字面量
布尔型 - 整型 - 浮点型
字符型 - 字符串型 - 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) 如果只有一个特化匹配模板参数,则使用该特化
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++ 标准。

DR 应用于 已发布的行为 正确行为
CWG 727 C++98
类作用域中不允许偏特化和完全特化
在任何范围内都允许
CWG 1315 C++98 模板参数不能在非类型
模板参数中使用,除了标识符表达式
只要可推断,表达式就可以
CWG 1495 C++11 涉及参数包时的规范不清楚 特化必须更特化
CWG 1711 C++14 缺少变量模板偏特化的规范 添加对变量模板的支持
CWG 1819 C++98 偏特化定义的可接受范围 使偏特化可以在
与主模板相同的范围内声明
CWG 2330 C++14 缺少对变量模板的引用 添加对变量模板的支持

[编辑] 参见