命名空间
变体
操作

模板

来自 cppreference.com
< cpp‎ | 语言
 
 
C++语言
 
 

模板是 C++ 中定义以下内容的实体:

(自 C++11 起)
(自 C++14 起)
(自 C++20 起)

模板由一个或多个 模板参数 参数化,这些参数分为三种类型:类型模板参数、非类型模板参数和模板模板参数。

当提供模板参数,或者仅对于 函数 (自 C++17 起) 模板,推断时,它们将被替换为模板参数以获得模板的 *特化*,即特定类型或特定函数左值。

也可以显式提供特化:完整特化 允许用于类、变量(自 C++14 起) 和函数模板,偏特化 仅允许用于类模板 和变量模板(自 C++14 起).

当在需要完整对象类型的上下文中引用类模板特化,或在需要函数定义存在的上下文中引用函数模板特化时,模板将被 *实例化*(实际上编译了它的代码),除非模板已经显式特化或显式实例化。类模板的实例化不会实例化任何成员函数,除非它们也被使用。在链接时,由不同翻译单元生成的相同实例化将被合并。

类模板的定义必须在隐式实例化点可见,这就是为什么模板库通常在头文件中提供所有模板定义的原因(例如,大多数 boost 库都是头文件库)。

内容

[编辑] 语法

template <parameter-list > requires-clause (可选) 声明 (1)
export template <parameter-list > 声明 (2) (直到 C++11)
template <parameter-list > concept concept-name = constraint-expression ; (3) (自 C++20 起)
parameter-list - 一个非空的用逗号分隔的 模板参数 列表,每个参数都是 非类型参数类型参数模板参数 或任何这些类型的 参数包(自 C++11 起).
requires-clause - (自 C++20 起) 一个 requires 子句,它指定了对模板参数的 约束
声明 - 声明一个类模板(包括结构体和联合体)、一个成员类或成员枚举类型、一个函数成员函数、一个命名空间作用域的静态数据成员、一个变量或类作用域的静态数据成员(自 C++14 起),或一个别名模板(自 C++11 起)。它也可以定义一个模板特化
概念名称
约束表达式
- 参见约束和概念

export 是一个可选的修饰符,它将模板声明为导出的(当与类模板一起使用时,它将声明其所有成员都被导出)。实例化导出模板的文件不需要包含其定义:声明就足够了。export 的实现很少见,并且在细节上彼此不同。

(直到 C++11)

[编辑] 模板标识符

模板标识符具有以下语法之一

模板名称 <模板参数列表 (可选)> (1)
operatorop <模板参数列表 (可选)> (2)
operator "" 标识符 <模板参数列表 (可选)> (3) (自 C++11 起)
(已弃用)
operator 用户定义的字符串字面量 <模板参数列表 (可选)> (4) (自 C++11 起)
1) 一个简单的模板标识符
2) 一个运算符函数模板标识符。
3,4) 一个字面量运算符 函数模板标识符。
模板名称 - 一个标识符,它命名一个模板
op - 一个可重载运算符
标识符 - 一个标识符
用户定义的字符串字面量 - "" 后跟一个标识符


一个命名类模板特化的简单模板标识符命名一个类。

一个命名别名模板特化的模板标识符命名一个类型。

一个命名函数模板特化的模板标识符命名一个函数。

如果满足以下所有条件,则模板标识符为有效 

  • 参数数量最多与参数数量相同或参数是一个模板参数包(自 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 起)
  • 一个模板实体的成员(例如,类模板的非模板成员函数)
  • 一个作为模板实体的枚举的枚举器
  • 在模板实体内定义或创建的任何实体:局部类、局部变量、友元函数等
  • 出现在模板实体声明中的 lambda 表达式的闭包类型
(自 C++11 起)

例如,在

template<typename T>
struct A
{
    void f() {}
};

函数 A::f 不是函数模板,但仍然被认为是模板化的。


一个模板函数是函数模板或模板化的函数。

一个模板类是类模板或模板化的类。

一个模板变量是变量模板或模板化的变量。

(自 C++14 起)

[编辑] 关键字

templateexport

[编辑] 缺陷报告

以下行为变更缺陷报告已追溯应用到以前发布的 C++ 标准。

DR 应用于 发布的行为 正确的行为
CWG 2293 C++98 确定模板
标识符是否有效的规则没有提供
提供
CWG 2682 C++98
C++14
模板函数/模板类
(C++98)/模板变量 (C++14) 的定义缺失
添加
P2308R1 C++98 如果它们的
相应非类型模板参数
不是模板参数等价的,则两个模板标识符不同
如果它们的相应
非类型模板参数值
不是模板参数等价的,则两个模板标识符不同

[编辑] 另见

C 文档 用于 泛型选择