模板

来自cppreference.com
< cpp‎ | language


 
 
C++ 语言
 
 

模板是一个 C++ 实体,它定义以下其一:

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

模板以一个或多个模板形参参数化,形参有三种:类型模板形参、非类型模板形参和模板模板形参。

当提供了模板实参,或当函数模板 (C++17 起)模板的模板实参被推导出时,它们替换各模板形参,以获得模板的一个特化,即一个特定类型或一个特定函数左值。

特化也可以显式提供:对类、变量 (C++14 起)和函数模板都允许全特化,只允许对类模板和变量模板 (C++14 起)部分特化

在要求完整对象类型的语境中引用某个类模板特化时,或在要求函数定义存在的语境中引用某个函数模板特化时,除非模板已经被显式特化或显式实例化,否则模板即被实例化(它的代码被实际编译)。类模板的实例化不会实例化其任何成员函数,除非它们也被使用。在链接时,不同翻译单元生成的相同实例被合并。

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

语法

template <形参列表 > requires子句 (可选) 声明 (1)
export template <形参列表 > 声明 (2) (C++11 前)
template <形参列表 > concept 概念名 = 约束表达式 ; (3) (C++20 起)
形参列表 - 非空的模板形参的逗号分隔列表,其中每项是非类型形参类型形参模板形参或任何这些的形参包之一。
requires子句 - (C++20 起) 指定了各模板实参上的约束requires子句
声明 - 成员类或成员枚举类型函数成员函数,命名空间作用域的静态数据成员,变量或类作用域的静态数据成员 (C++14 起)别名模板 (C++11 起)的声明。它也可以定义模板特化
概念名
约束表达式
- 约束与概念

export 是一个可选的修饰符,声明模板被导出(用于类模板时,它声明该类的所有成员也被导出)。对被导出模板进行实例化的文件不需要包含其定义:有声明就足够了。export 的实现稀少而且在细节上互不一致。

(C++11 前)

模板标识

模板标识具有以下语法:

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

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

指名别名模版特化的模板标识指名一个类型。

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

模板标识在满足以下所有条件时合法

  • 实参数量不大于形参数量,或有一个形参是模板形参包 (C++11 起)
  • 每个没有默认模板实参的不可推导的非包 (C++11 起)形参都有一个实参。
  • 每个模板实参都与对应的模板形参相匹配。
  • 替换每个模板实参到它的后续模板形参(如果存在)中均成功。
  • 如果模板标识非待决,那么它关联得约束需要按下述要求得以满足。
(C++20 起)

无效的简单模板标识是编译时错误,除非它指名的是函数模板特化(此时适用 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>;           // OK

如果在简单模板标识的模板名 指名受约束的非函数模板或受约束的模板模板形参,但不是作为未知特化的成员的成员模板,而且简单模板标识中的所有模板实参均非待决,那么必须满足受约束模板的各项关联约束:

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; };         // OK:不要求满足
 
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 起)模板
(C++20 起)
  • 模板化实体的成员(例如类模板的非模板成员函数)
  • 作为模板化实体的枚举的枚举项
  • 任何模板化实体中定义或创建的实体:局部类,局部变量,友元函数,等等
  • 模板化实体的声明中出现的 lambda 表达式的闭包类型
(C++11 起)

例如,在以下模板中:

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

函数 A::f 不是函数模板,但它仍然会被当做是模板化的。


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

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

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

(C++14 起)

关键词

template, export

缺陷报告

下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。

缺陷报告 应用于 出版时的行为 正确行为
CWG 2293 C++98 未提供判断模板标识是否合法的规则 已提供
CWG 2682 C++98
C++14
缺失了模板化函数/模板类(C++98)/模板化变量(C++14)的定义 已补充
P2308R1 C++98 当两个模板标识的对应非类型模板实参并非模板实参等价时,它们不同 当对应非类型模板形参值并非模板实参等价时,它们不同

参阅