模板

来自cppreference.com
< cpp‎ | language

模板是定义下列之一的 C++ 实体:

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

当提供模板实参,或仅对于函数 (C++17 起)模板推导模板实参时,它们替换模板形参,以获得模板的特化,即一个特定类型或一个特定函数左值。特化亦可显式提供:允许对类和函数模板全特化,只允许对类模板偏特化

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

模板的定义必须在隐式实例化点可见,此为模板库典型地提供所有模板定义于其头文件的原因(例如大多数 boost 库仅有头文件

[编辑] 语法

template < 参数列表 > 声明 要求子句(C++20)(可选) (1)
export template < 参数列表 > 声明 (2) (C++11 前)
template < 参数列表 > concept 概念名 = 制约表达式 ; (3) (C++20 起)
声明 - 一个类(含 struct 和 union )成员模板或成员枚举类型函数成员函数、命名空间作用域的静态数据成员变量或类作用域的静态数据成员 (C++14 起)别名模板 (C++11 起)的声明。它亦可定义模板特化
参数列表 - 非空的模板形参的逗号分隔列表,每项是非类型参数类型参数模板参数或任何这些的参数包之一。
概念名
制约表达式
- 制约与概念 (C++20 起)

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

(C++11 前)

模板形参列表可以后随一个可选的 要求子句 ,它指定模板实参上的制约

(C++20 起)


[编辑] template-id

template-name < parameter-list >
template-name - 指名模板的标识符(该情况下称之为 "simple-template-id" )或重载运算符模板或用户定义字面量模板的名称。

指名模板特化的 simple-template-id 命名一个类。

指名别名模版特化的 template-id 指名一个类型。

指名函数模板特化的 template-id 命名一个函数。

template-id 仅若符合下列条件才合法

  • 实参数量不多于形参,或形参是模板形参包,
  • 每个无默认模板实参的不可推导的非包形参都有一个实参,
  • 每个模板实参匹配对应的模板形参,
  • 替换每个模板实参到后续模板形参(若存在)中均成功,而且
  • (C++20)template-id 非依赖,则满足指定如下的关联制约。

非法的 simple-template-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>;           // OK

simple-template-id 的模板名指名受制约的非函数模板或受制约的模板模板形参,但不是作为未知特化的成员的成员模板,而且 simple-template-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; };            // 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 起)

[编辑] 模板化实体

模板化实体(某些资料称之为 "temploid" )是任何在模板定义内定义(或对于 lambda-expression 是创建)的实体。下列所有实体是模板化实体:

  • 类/函数/变量模板
  • 模板化实体的成员(例如类模板的非模板成员函数)
  • 作为模板化实体的枚举的枚举项
  • 任何模板化实体中定义或创建的实体:局部类、局部变量、友元函数等
  • 模板化实体的定义中出现的 lambda 表达式的闭包类型

例如,以下模板中:

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

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