Templates
模板是定义以下内容之一的C++实体:
|
(自 C++11 起) |
|
(since C++14) |
|
(自 C++20 起) |
模板通过一个或多个 模板参数 进行参数化,共有三种类型:类型模板参数、常量模板参数和模板模板参数。
当提供了模板参数时,或者对于 函数 和 类 (C++17 起) 模板(仅限推导情况),这些参数会被替换到模板形参中,从而获得模板的 特化 ,即一个具体类型或特定的函数左值。
特化也可以显式提供: 全特化 允许用于类 、变量 (C++14 起) 和函数模板, 偏特化 仅允许用于类模板 和变量模板 (C++14 起) 。
当类模板特化在需要完整对象类型的上下文中被引用时,或当函数模板特化在需要存在函数定义的上下文中被引用时,该模板会被 实例化 (其代码实际被编译),除非该模板已被显式特化或显式实例化。类模板的实例化不会实例化其任何成员函数,除非这些成员函数也被使用。在链接阶段,不同翻译单元生成的相同实例化会被合并。
类模板的定义在隐式实例化点必须可见,这就是为什么模板库通常将所有模板定义放在头文件中(例如 大多数boost库仅包含头文件 )。
目录 |
语法
template <
形参列表
>
requires-子句
(可选)
声明
|
(1) | ||||||||
export template <
形参列表
>
声明
|
(2) | (C++11前) | |||||||
template <
形参列表
> concept
概念名
=
约束表达式
;
|
(3) | (C++20起) | |||||||
| parameter-list | - | 一个非空的逗号分隔的 模板形参 列表,每个形参可以是 常量形参 、 类型形参 、 模板形参 ,或是这些形参类型的 形参包 (C++11 起) 。 |
| requires-clause | - | (C++20 起) 用于指定模板实参 约束 的 requires 子句 。 |
| declaration | - | 类(含 struct 与 union) 、 成员类或成员枚举类型 、 函数 或 成员函数 、命名空间作用域的静态数据成员 、类作用域的变量或静态数据成员 (C++14 起) 、或 别名模板 (C++11 起) 的声明。也可用于定义 模板特化 。 |
|
concept-name
constraint-expression |
- | 参见 约束与概念 |
|
export 是一个可选修饰符,用于将模板声明为 已导出 (当用于类模板时,会同时声明其所有成员为已导出)。实例化已导出模板的文件无需包含其定义:仅声明即可。实际实现中 export 功能较为罕见,且不同实现细节存在分歧。 |
(C++11 前) |
|
本节内容不完整
原因:核心语法、模板参数及实例化,需提取类模板与函数模板的通用内容 |
模板标识符
模板标识符具有以下语法之一:
模板名
<
模板实参列表
(可选)
>
|
(1) | ||||||||
operator
运算符
<
模板实参列表
(可选)
>
|
(2) | ||||||||
operator ""
标识符
<
模板实参列表
(可选)
>
|
(3) |
(C++11 起)
(已弃用) |
|||||||
operator
用户定义字符串字面量
<
模板实参列表
(可选)
>
|
(4) | (C++11 起) | |||||||
| template-name | - | 用于命名模板的 标识符 |
| op | - | 可重载的 运算符 |
| identifier | - | 标识符 |
| user-defined-string-literal | - | "" 后接标识符 |
一个简单的模板标识符,若其命名的是类模板特化,则它命名的是一个类。
一个命名别名模板特化的模板标识符表示一个类型。
命名函数模板特化的模板标识符表示一个函数。
若满足以下所有条件,则模板标识符为 有效 :
- 实参数量最多与形参数量相等 或存在模板 形参包 (C++11 起) 。
- 每个不可推导 非包 (C++11 起) 且无默认模板实参的形参都对应一个实参。
- 每个模板实参都与对应的模板形参匹配。
- 每个模板实参代入后续模板形参(若存在)的替换操作均成功。
|
(since C++20) |
无效的简单模板id属于编译时错误,除非它命名的是函数模板特化(此时可能适用 SFINAE 规则)。
template<class T, T::type n = 0> class X; struct S { using type = int; }; using T1 = X<S, int, int>; // 错误:参数过多 using T2 = X<>; // 错误:第一个模板参数无默认参数 using T3 = X<1>; // 错误:值 1 与类型参数不匹配 using T4 = X<int>; // 错误:第二个模板参数替换失败 using T5 = X<S>; // 正确
|
当简单模板id的 模板名 指向一个受约束的非函数模板或受约束的模板模板参数,但非未知特化的成员模板,且简单模板id中的所有模板参数均为非依赖参数时,必须满足该受约束模板的关联约束: template<typename T> concept C1 = sizeof(T) != sizeof(int); template<C1 T> struct S1 {}; template<C1 T> using Ptr = T*; S1<int>* p; // 错误:约束未满足 Ptr<int> p; // 错误:约束未满足 template<typename T> struct S2 { Ptr<int> x; }; // 错误,不要求诊断 template<typename T> struct S3 { Ptr<T> x; }; // 正确,不要求满足约束 S3<int> x; // 错误:约束未满足 template<template<C1 T> class X> struct S4 { X<int> x; // 错误,不要求诊断 }; template<typename T> concept C2 = sizeof(T) == 1; template<C2 T> struct S {}; template struct S<char[2]>; // 错误:约束未满足 template<> struct S<char[2]> {}; // 错误:约束未满足 |
(C++20 起) |
若满足以下所有条件,则两个模板标识符被视为 相同 :
- 它们的 模板名称 或运算符引用的是同一个模板。
- 它们对应的类型模板参数是相同的类型。
- 由它们对应的常量模板参数所确定的模板参数值满足 模板参数等价 关系。
- 它们对应的模板模板参数引用的是同一个模板。
两个相同的模板标识符指向同一个 变量、 (C++14 起) 类或函数。
模板化实体
一个 模板化实体 (或在某些资料中称为“模板体”)是指任何在模板定义内部定义 (或对于 lambda表达式 而言是创建) (C++11 起) 的实体。以下所有内容都属于模板化实体:
- 类/函数 /变量 (C++14 起) 模板
|
(since C++20) |
- 模板化实体的成员(例如类模板的非模板成员函数)
- 作为模板化实体的枚举类型的枚举项
- 在模板化实体内部定义或创建的任何实体:局部类、局部变量、友元函数等
|
(since C++11) |
例如,在
template<typename T> struct A { void f() {} };
函数
A::f
并非函数模板,但仍被视为具有模板特性。
一个
模板化函数
是指函数模板或经过模板化的函数。
一个 模板类 是指类模板或经过模板化的类。
|
模板化变量 是指变量模板或经过模板化的变量。 |
(since C++14) |
关键词
缺陷报告
以下行为变更缺陷报告被追溯应用于先前发布的C++标准。
| 缺陷报告 | 适用标准 | 发布时行为 | 正确行为 |
|---|---|---|---|
| CWG 2293 | C++98 | 未提供判断模板标识符是否有效的规则 | 已提供 |
| CWG 2682 |
C++98
C++14 |
缺少模板化函数/模板类
(C++98)/模板化变量(C++14)的定义 |
已添加 |
| P2308R1 | C++98 |
若对应常量模板实参
非模板实参等价,则两个模板标识符不同 |
若对应常量模板参数值
非模板实参等价,则两者不同 |
参见
|
C 文档
关于
泛型选择
|