模板
模板是一个 C++ 实体,它定义了以下之一
|
(C++11 起) |
|
(C++14 起) |
|
(C++20 起) |
模板由一个或多个模板参数参数化,模板参数有三种:类型模板参数、非类型模板参数和模板模板参数。
当提供了模板实参,或者对于函数和类(C++17 起)模板,仅推导实参时,这些实参将替换模板参数以获得模板的*特化*,即特定类型或特定函数左值。
特化也可以显式提供:类、变量(C++14 起)和函数模板允许完全特化,部分特化只允许类模板和变量模板(C++14 起)。
当在需要完整对象类型的上下文中引用类模板特化,或者在需要函数定义存在的上下文中引用函数模板特化时,模板将被*实例化*(其实际代码被编译),除非该模板已被显式特化或显式实例化。类模板的实例化不会实例化其任何成员函数,除非它们也被使用。在链接时,由不同翻译单元生成的相同实例化会被合并。
类模板的定义必须在隐式实例化点可见,这就是为什么模板库通常在头文件中提供所有模板定义(例如,大多数 Boost 库都是仅头文件)。
目录 |
[编辑] 语法
template < 参数列表 > requires-clause (可选) 声明 |
(1) | ||||||||
export template < 参数列表 > 声明 |
(2) | (C++11 前) | |||||||
template < 参数列表 > concept 概念名 = 约束表达式 ; |
(3) | (C++20 起) | |||||||
参数列表 | - | 非空的逗号分隔的模板参数列表,每个参数要么是非类型参数、类型参数、模板参数,或其中任何一种的参数包(C++11 起)。 |
requires-clause | - | (C++20 起) 一个requires-clause,指定模板实参的约束。 |
声明 | - | 类(包括 struct 和 union)、成员类或成员枚举类型、函数或成员函数、命名空间范围的静态数据成员、类范围的变量或静态数据成员(C++14 起),或别名模板(C++11 起)的声明。它也可以定义模板特化。 |
概念名 约束表达式 |
- | 参见约束与概念 |
export 是一个可选的修饰符,它将模板声明为*导出*(当与类模板一起使用时,它也声明其所有成员为导出)。实例化导出模板的文件不需要包含其定义:声明就足够了。export 的实现很少见,并且在细节上存在分歧。 |
(C++11 前) |
本节不完整 原因:核心语法、模板参数和实例化,采用 class_template 和 function_template 之间的共同内容 |
[编辑] 模板标识符
模板标识符具有以下语法之一
模板名 < 模板实参列表 (可选)> |
(1) | ||||||||
operator 操作符 < 模板实参列表 (可选)> |
(2) | ||||||||
operator "" 标识符 < 模板实参列表 (可选)> |
(3) | (C++11 起) (已弃用) | |||||||
operator 用户定义字符串字面量 < 模板实参列表 (可选)> |
(4) | (C++11 起) | |||||||
模板名 | - | 命名模板的标识符 |
操作符 | - | 可重载操作符 |
标识符 | - | 一个标识符 |
用户定义字符串字面量 | - | "" 后跟一个标识符 |
命名类模板特化的简单模板标识符命名一个类。
命名别名模板特化的模板标识符命名一个类型。
命名函数模板特化的模板标识符命名一个函数。
如果满足以下所有条件,则模板标识符是*有效*的:
- 实参数量至多与参数数量相同,或参数是模板参数包(C++11 起)。
- 对于每个没有默认模板实参的不可推导非包(C++11 起)参数,都有一个实参。
- 每个模板实参与对应的模板参数匹配。
- 将每个模板实参代入后续模板参数(如果有)成功。
|
(C++20 起) |
无效的简单模板 id 是编译时错误,除非它命名函数模板特化(在这种情况下,SFINAE 可能适用)。
template<class T, T::type n = 0> class X; struct S { using type = int; }; using T1 = X<S, int, int>; // error: too many arguments using T2 = X<>; // error: no default argument for first template parameter using T3 = X<1>; // error: value 1 does not match type-parameter using T4 = X<int>; // error: substitution failure for second template parameter using T5 = X<S>; // OK
当简单模板 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; // error: constraints not satisfied Ptr<int> p; // error: constraints not satisfied template<typename T> struct S2 { Ptr<int> x; }; // error, no diagnostic required template<typename T> struct S3 { Ptr<T> x; }; // OK, satisfaction is not required S3<int> x; // error: constraints not satisfied template<template<C1 T> class X> struct S4 { X<int> x; // error, no diagnostic required }; template<typename T> concept C2 = sizeof(T) == 1; template<C2 T> struct S {}; template struct S<char[2]>; // error: constraints not satisfied template<> struct S<char[2]> {}; // error: constraints not satisfied |
(C++20 起) |
如果满足以下所有条件,则两个模板标识符是*相同*的:
- 它们的模板名或操作符指向同一个模板。
- 它们对应的类型模板实参是相同的类型。
- 由它们对应的非类型模板实参确定的模板参数值是模板实参等价的。
- 它们对应的模板模板实参指向同一个模板。
两个相同的模板标识符指向相同的变量、(C++14 起)类或函数。
[编辑] 模板化实体
一个*模板化实体*(或在某些资料中称为“temploid”)是在模板定义中定义(或,对于lambda 表达式,创建)(C++11 起)的任何实体。以下所有都是模板化实体:
- 类/函数/变量(C++14 起)模板
|
(C++20 起) |
- 模板化实体的成员(例如类模板的非模板成员函数)
- 作为模板化实体的枚举的枚举器
- 在模板化实体内定义或创建的任何实体:局部类、局部变量、友元函数等。
|
(C++11 起) |
例如,在
template<typename T> struct A { void f() {} };
函数A::f
不是函数模板,但仍被认为是模板化的。
模板化函数是一个函数模板或一个模板化的函数。
模板化类是一个类模板或一个模板化的类。
模板化变量是一个变量模板或一个模板化的变量。 |
(C++14 起) |
[编辑] 关键词
[编辑] 缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 发布时的行为 | 正确的行为 |
---|---|---|---|
CWG 2293 | C++98 | 没有提供确定模板 标识符是否有效的规则 |
已提供 |
CWG 2682 | C++98 C++14 |
缺少模板化函数/模板类 (C++98)/模板化变量(C++14)的定义 |
已添加 |
P2308R1 | C++98 | 如果两个模板标识符的 对应的非类型模板实参 不模板实参等价,则它们是不同的 |
如果它们的对应的 非类型模板参数值 不模板实参等价,则它们是不同的 |
[编辑] 另见
C 文档 关于 通用选择
|