Explicit (full) template specialization
允许为给定的模板参数集自定义模板代码。
目录 |
语法
template <>
声明
|
|||||||||
以下任意项均可完全特化:
- 函数模板
- 类模板
- 变量模板 (C++14 起)
- 类模板的 成员函数
- 类模板的 静态数据成员
- 类模板的 成员类
- 类模板的成员 枚举
- 类或类模板的 成员类模板
- 类或类模板的 成员函数模板
- 类或类模板的 成员变量模板 (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> // 主模板 class X { /*...*/ }; template<> // 同一命名空间内的特化 class X<int> { /*...*/ }; template<class T> // 主模板 class Y { /*...*/ }; template<> // 前向声明 double 类型的特化 class Y<double>; } template<> // 正确:同一命名空间内的特化 class N::Y<double> { /*...*/ };
特化必须在导致隐式实例化的首次使用之前声明,在发生此类使用的每个翻译单元中:
class String {}; template<class T> class Array { /*...*/ }; template<class T> // 主模板 void sort(Array<T>& v) { /*...*/ } void f(Array<String>& v) { sort(v); // 隐式实例化 sort(Array<String>&), } // 使用 sort() 的主模板 template<> // 错误:对 sort(Array<String>) 的显式特化 void sort<String>(Array<String>& v); // 出现在隐式实例化之后
已声明但未定义的模板特化可以像其他 不完整类型 一样使用(例如可以使用它的指针和引用):
template<class T> // 主模板 class X; template<> // 特化(已声明但未定义) class X<int>; X<int>* p; // 正确:指向不完整类型的指针 X<int> x; // 错误:不完整类型的对象
函数模板
或变量模板
(C++14 起)
的显式特化是否为
inline
/
constexpr
(C++11 起)
/
constinit
/
consteval
(C++20 起)
由显式特化自身决定,与主模板是否声明该说明符无关。
类似地,模板声明中出现的
属性
不会影响该模板的显式特化:
(C++11 起)
template<class T> void f(T) { /* ... */ } template<> inline void f<>(int) { /* ... */ } // 正确,使用 inline template<class T> inline T g(T) { /* ... */ } template<> int g<>(int) { /* ... */ } // 正确,未使用 inline template<typename> [[noreturn]] void h([[maybe_unused]] int i); template<> void h<int>(int i) { // [[noreturn]] 无作用,但 [[maybe_unused]] 仍有效 }
函数模板的显式特化
当特化函数模板时,若 模板实参推导 能够从函数实参中获取模板参数,则可省略其模板实参:
template<class T> class Array { /*...*/ }; template<class T> // 主模板 void sort(Array<T>& v); template<> // 针对 T = int 的特化 void sort(Array<int>&); // 无需编写 // template<> void sort<int>(Array<int>&);
具有相同名称和相同参数列表的函数并非特化(参见
function template
中的
函数模板重载
)。
默认函数参数 不能在函数模板的显式特化、成员函数模板以及类模板的成员函数中指定,当类被隐式实例化时。
显式特化不能作为 友元声明 。
|
本节内容不完整
原因:需审查不同C++版本中异常规范要求 |
特化的成员
当在显式特化的类模板外部定义其成员时,除以下情况外不使用 template <> 语法:若该成员属于显式特化的成员类模板(其本身作为类模板被特化),此时必须使用该语法,否则按嵌套模板要求,定义需以 template < parameters > 作为起始
template<typename T> struct A { struct B {}; // 成员类 template<class U> // 成员类模板 struct C {}; }; template<> // 特化 struct A<int> { void f(int); // 特化的成员函数 }; // 特化的成员不需要使用 template<> void A<int>::f(int) { /* ... */ } template<> // 成员类的特化 struct A<char>::B { void f(); }; // 特化成员类的成员同样不需要使用 template<> void A<char>::B::f() { /* ... */ } template<> // 成员类模板的特化 template<class U> struct A<char>::C { void f(); }; // 当定义被显式特化为类模板的成员类模板的成员时,需要使用 template<> template<> template<class U> void A<char>::C<U>::f() { /* ... */ }
模板静态数据成员的显式特化如果声明包含初始化器则为定义;否则为声明。这些定义必须使用花括号进行默认初始化:
template<> X Q<int>::x; // 静态成员的声明 template<> X Q<int>::x (); // 错误:函数声明 template<> X Q<int>::x {}; // 默认初始化静态成员的定义
类模板的成员或成员模板可以为类模板的给定隐式实例化显式特化,即使该成员或成员模板是在类模板定义中定义的。
template<typename T> struct A { void f(T); // 成员函数,在主模板中声明 void h(T) {} // 成员函数,在主模板中定义 template<class X1> // 成员模板 void g1(T, X1); template<class X2> // 成员模板 void g2(T, X2); }; // 成员函数的特化 template<> void A<int>::f(int); // 成员特化即使在类内定义也有效 template<> void A<int>::h(int) {} // 类外成员模板定义 template<class T> template<class X1> void A<T>::g1(T, X1) {} // 成员模板特化 template<> template<class X1> void A<int>::g1(int, X1); // 成员模板特化 template<> template<> void A<int>::g2<char>(int, char); // 对应 X2 = char // 相同功能,使用模板参数推导 (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>();
` 标签内的 C++ 代码块中,根据要求保留了所有代码和 HTML 标签的原始格式,未进行任何翻译。
在此类嵌套声明中,某些层级可能保持未特化状态(除非当外层类未特化时,不能在命名空间作用域特化其类成员模板)。对于每个这样的层级,声明需要 template < arguments > ,因为此类特化本身即是模板:
template<class T1> class A { template<class T2> class B { template<class T3> // 成员模板 void mf1(T3); void mf2(); // 非模板成员 }; }; // 特化 template<> // 针对特化的 A template<class X> // 针对未特化的 B class A<int>::B { template<class T> void mf1(T); }; // 特化 template<> // 针对特化的 A template<> // 针对特化的 B template<class T> // 针对未特化的 mf1 void A<int>::B<double>::mf1(T t) {} // 错误:B<double> 是特化的成员模板,因此其所属的 A 也必须特化 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 |
主模板的
constinit
和
consteval
是否
会继承到其显式特化中不明确 |
不继承 |
| CWG 2604 | C++11 | 主模板的属性是否会继承到其显式特化中不明确 | 不继承 |