C++ 参考手册
- C++11
- C++14
- C++17
- C++20
- C++ 编译器支持情况表
- 独立与宿主实现
- C++ 语言
- 变量模板(C++14 起)
- 整数字面量
- 聚合初始化
- 比较运算符
- 默认比较(C++20 起)
- 转义序列
- for 循环
- while 循环
- 用户定义转换
- SFINAE
- 主函数
- ASCII 码表
- 标识符
- 类型
- 内存模型
- 对象
- 基本概念
- 表达式
- 声明
- 初始化
- 函数
- 语句
- 类
- 运算符重载
- 模板
- 异常
- 事务性内存
- 占位符类型说明符 (C++11 起)
- decltype 说明符
- 函数声明
- final 说明符 (C++11 起)
- override 说明符(C++11 起)
- 引用声明
- 移动构造函数
- 移动赋值运算符
- 枚举声明
- constexpr 说明符(C++11 起)
- 列表初始化 (C++11 起)
- 构造函数与成员初始化器列表
- using 声明
- nullptr,指针字面量
- 基础类型
- 类型别名,别名模版 (C++11 起)
- 形参包
- 联合体声明
- 字符串字面量
- 用户定义字面量 (C++11 起)
- 属性说明符序列(C++11 起)
- Lambda 表达式 (C++11 起)
- noexcept 说明符 (C++11 起)
- noexcept 运算符 (C++11 起)
- alignof 运算符(C++11 起)
- alignas 说明符 (C++11 起)
- 存储类说明符
- 基于范围的 for 循环 (C++11 起)
- static_assert 声明
- 隐式转换
- 代用运算符表示
- 自增/自减运算符
- 折叠表达式(C++17 起)
- 类模板实参推导(C++17 起)
- 模板形参与模板实参
- if 语句
- inline 说明符
- 结构化绑定声明 (C++17 起)
- switch 语句
- 字符字面量
- 命名空间
- 求值顺序
- 复制消除
- consteval 说明符 (C++20 起)
- constinit 说明符 (C++20 起)
- 协程 (C++20)
- 模块 (C++20 起)
- 约束与概念 (C++20 起)
- new 表达式
- do-while 循环
- continue 语句
- break 语句
- goto 语句
- return 语句
- 动态异常说明
- throw 表达式
- try 块
- 命名空间别名
- 类声明
- cv(const 与 volatile)类型限定符
- 默认初始化
- 值初始化(C++03 起)
- 零初始化
- 复制初始化
- 直接初始化
- 常量初始化
- 引用初始化
- 值类别
- C++ 运算符优先级
- 布尔字面量
- 浮点字面量
- typedef 说明符
- 显式类型转换
- static_cast 转换
- dynamic_cast 转换
- const_cast 转换
- reinterpret_cast 转换
- delete 表达式
- 构造函数与成员初始化器列表
- this 指针
- 访问说明符
- 友元声明
- virtual 函数说明符
- explicit 说明符
- 静态成员
- 默认构造函数
- 复制构造函数
- 复制赋值运算符
- 析构函数
- 类模板
- 函数模板
- 显式(全)模板特化
- 汇编声明
- C++ 的历史
- 作用域
- 生存期
- 定义与单一定义规则(ODR)
- 名字查找
- 有限定的名字查找
- 无限定的名字查找
- 如同规则
- 未定义行为
- 翻译阶段
- 常量表达式
- 赋值运算符
- 算术运算符
- 逻辑运算符
- 成员访问运算符
- 其他运算符
- sizeof 运算符
- typeid 运算符
- 指针声明
- 数组声明
- 语言链接
- 详述类型说明符
- 默认实参
- 变长实参
- 实参依赖查找
- 重载决议
- 重载函数的地址
- 注入类名
- 非静态数据成员
- 非静态成员函数
- 嵌套类
- 派生类
- 空基类优化
- 抽象类
- 位域
- 转换构造函数
- 成员模板
- 模板实参推导
- 部分模板特化
- sizeof... 运算符
- 待决名
- 函数 try 块
- 扩充命名空间 std
- 字母缩写
- RAII
- 三/五/零之法则
- PImpl
- 零开销原则
- 类型
- 隐式转换
- 注释
- C++ 关键词
- 预处理器
- C++ 标准库头文件
- 具名要求
- 功能特性测试 (C++20)
- 工具库
- 类型支持(基本类型、RTTI、类型特性)
- 概念库 (C++20)
- 错误处理
- 动态内存管理
- 日期和时间工具
- 字符串库
- 容器库
- 迭代器库
- 范围库 (C++20)
- 算法库
- 数值库
- 输入/输出库
- 文件系统库
- 本地化库
- 正则表达式库
- 原子操作库
- 线程支持库
- 实验性 C++ 特性
- 有用的资源
- 索引
- std 符号索引
- 协程支持 (C++20)
- C++ 关键词
显式(全)模板特化
允许对给定的模板实参集定制模板代码。
语法
template <> 声明
|
|||||||||
以下任何一项均可以完全特化:
例如,
#include <iostream> template<typename T> // 主模板 struct is_void : std::false_type { }; template<> // 对 T = void 显式特化 struct is_void<void> : std::true_type { }; int main() { // 对于任何非 void 的类型 T,该类均派生自 false_type std::cout << is_void<char>::value << '\n'; // 但当 T 为 void 时类派生自 true_type std::cout << is_void<void>::value << '\n'; }
细节
显式特化可以在可以定义其主模板的任何作用域中声明(这可以不同于定义其主模板定义的作用域;例如同成员模板的类外特化)。显式特化必须出现在非特化模板声明后。
namespace N { template<class T> class X { /*...*/ }; // 主模板 template<> class X<int> { /*...*/ }; // 同命名空间中的特化 template<class T> class Y { /*...*/ }; // 主模板 template<> class Y<double>; // 对 double 特化的前置声明 } template<> class N::Y<double> { /*...*/ }; // OK:同命名空间中的特化
特化必须在第一条导致隐式实例化的使用之前,在每个发生这种使用的翻译单元中声明:
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; // OK:指向不完整类型的指针 X<int> x; // 错误:不完整类型的对象
函数模板的显式特化
当特化函数模板时,若模板实参推导能从函数实参予以提供,则可忽略其实参:
template<class T> class Array { /*...*/ }; template<class T> void sort(Array<T>& v); // 主模板 template<> void sort(Array<int>&); // 对 T = int 的特化 // 不需要写为 // template<> void sort<int>(Array<int>&);
与某个特化带有相同名字和相同形参列表的函数不是特化(见函数模板中的重载)
仅当函数模板的显式特化声明为带 inline 说明符(或定义为弃置)时,它是内联函数,主模板是否为内联对其没有影响。
不能在函数模板,成员函数模板,以及在隐式实例化类时的类模板的成员函数的显式特化中指定默认函数实参。
显式特化不能是友元声明。
若主模板具有并非 noexcept(false)
的异常说明,则显式特化必须具有兼容的异常说明。
特化的成员
在类体外定义显式特化的类模板的成员时,不使用 template <> 语法,除非它是某个被特化为类模板的显式特化的成员类模板的成员,因为其他情况下,语法会要求这种定义以嵌套模板所要求的 template<形参> 开始
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); // 成员特化 OK,即使定义于类中 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> class A { template<class T2> class B { void mf(); }; }; template<> template<> class A<int>::B<double>; template<> template<> void A<char>::B<char>::mf();
在这种嵌套声明中,某些层次可保留不特化(但若其外围类未被特化,则不能特化类成员模板)。对于每个这种层次,声明需要 template<实参>,因为这种特化自身也是模板:
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++ 标准。
DR | 应用于 | 出版时的行为 | 正确行为 |
---|---|---|---|
CWG 727 | C++14 | 不允许在类作用域的全特化,即使允许部分特化 | 允许在任何作用域的全特化 |