C++
编译器支持
自由(freestanding)与宿主(hosted)
语言
标准库
标准库头文件
具名要求
特性测试宏 (C++20)
语言支持库
概念库 (C++20)
诊断库
内存管理库
元编程库 (C++11)
通用工具库
容器库
迭代器库
范围库 (C++20)
算法库
字符串库
文本处理库
数值库
日期和时间库
输入/输出库
文件系统库 (C++17)
并发支持库 (C++11)
执行控制库 (C++26)
技术规范
符号索引
外部库
[编辑] C++ 语言
通用主题
预处理器
注释
关键词
转义序列
流程控制
条件执行语句
if
switch
迭代语句(循环)
for
range-for (C++11)
while
do-while
跳转语句
continue - break
goto - return
函数
函数声明
Lambda 函数表达式
inline 说明符
动态异常规范 (直到 C++17*)
noexcept 说明符 (C++11)
异常
throw 表达式
try 块
catch 处理程序
命名空间
命名空间声明
命名空间别名
类型
基本类型
枚举类型
函数类型
类/结构体类型
联合类型
说明符
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
存储期说明符
初始化
默认初始化
值初始化
零初始化
复制初始化
直接初始化
聚合初始化
列表初始化 (C++11)
常量初始化
引用初始化
表达式
值类别
求值顺序
运算符
运算符优先级
替代表示
字面量
布尔 - 整型 - 浮点型
字符 - 字符串 - nullptr (C++11)
用户定义 (C++11)
工具
属性 (C++11)
类型
typedef 声明
类型别名声明 (C++11)
类型转换
隐式转换
static_cast
const_cast
显式转换
dynamic_cast
reinterpret_cast
内存分配
new 表达式
delete 表达式
类
类声明
构造函数
this 指针
访问说明符
friend 说明符
类特有的函数属性
虚函数
override 说明符 (C++11)
final 说明符 (C++11)
explicit (C++11)
static
特殊成员函数
默认构造函数
复制构造函数
移动构造函数 (C++11)
复制赋值
移动赋值 (C++11)
析构函数
模板
类模板
函数模板
模板特化
参数包 (C++11)
杂项
内联汇编
C++ 历史
[编辑] 声明
概览
声明语法
声明说明符序列
声明符
冲突声明
说明符
typedef
inline
virtual 函数说明符
explicit 函数说明符
friend
constexpr(C++11)
consteval(C++20)
constinit(C++20)
存储类说明符
翻译单元局部 (C++20)
class/struct
union
enum
decltype(C++11)
auto(C++11)
alignas(C++11)
constvolatile
包索引说明符 (C++26)
细化类型说明符
属性 (C++11)
声明符
引用
指针
数组
块声明
简单声明
→结构化绑定声明 (C++17)
别名声明 (C++11)
命名空间别名定义
using 声明
using 指令
static_assert 声明 (C++11)
asm 声明
不透明枚举声明 (C++11)
其他声明
命名空间定义
函数声明
类模板声明
函数模板声明
显式模板实例化 (C++11)
显式模板特化
链接规范
属性声明 (C++11)
空声明
[编辑] 表达式
通用
值类别
求值顺序
常量表达式
主表达式
Lambda 表达式 (C++11)
Requires 表达式 (C++20)
包索引表达式 (C++26)
潜在求值表达式
字面量
整数字面量
浮点数字面量
布尔字面量
字符字面量
转义序列
字符串字面量
空指针字面量 (C++11)
用户定义字面量 (C++11)
运算符
赋值运算符
递增和递减
算术运算符
逻辑运算符
比较运算符
成员访问运算符
其他运算符
new 表达式
delete 表达式
throw 表达式
alignof
sizeof
sizeof... (C++11)
typeid
noexcept (C++11)
折叠表达式 (C++17)
运算符的替代表示
优先级和结合性
运算符重载
默认比较 (C++20)
转换
隐式转换
显式转换
常用算术转换
用户定义转换
const_cast
static_cast
dynamic_cast
reinterpret_cast
[编辑] 模板
参数和实参
类模板
函数模板
类成员模板
变量模板 (C++14)
模板参数推导
类模板实参推导 (C++17)
显式(完全)特化
部分特化
依赖名称
包 (C++11)
sizeof... (C++11)
折叠表达式 (C++17)
包索引 (C++26)
SFINAE
约束和概念 (C++20)
Requires 表达式 (C++20)
[编辑]
包是定义以下内容之一的 C++ 实体
参数包
模板参数包函数参数包
lambda 初始化捕获包
(C++20 起)
结构化绑定包
(C++26 起)
模板参数包是接受零个或多个模板实参(非类型、类型或模板)的模板参数。函数参数包是接受零个或多个函数实参的函数参数。
lambda 初始化捕获包是一种 lambda 捕获,它为其初始化器中包展开的每个元素引入一个初始化捕获。
(C++20 起)
结构化绑定包是结构化绑定声明中的一个标识符,它引入零个或多个结构化绑定。
(C++26 起)
包的元素数量等于
如果包是模板或函数参数包,则为参数包提供的实参数量,
如果包是 lambda 初始化捕获包,则为其初始化器中包展开的元素数量,
(C++20 起)
如果包是结构化绑定包,则为初始化器的结构化绑定大小减去结构化绑定声明中非包元素的数量。
(C++26 起)
具有至少一个参数包的模板称为 可变参数模板。
目录
1 语法
2 解释
3 包展开
4 展开位置
4.1 函数实参列表
4.2 带括号的初始化器
4.3 花括号括起来的初始化器
4.4 模板实参列表
4.5 函数参数列表
4.6 模板参数列表
4.7 基类说明符和成员初始化器列表
4.8 Lambda 捕获
4.9 sizeof... 运算符
4.10 动态异常规范
4.11 对齐说明符
4.12 属性列表
4.13 折叠表达式
4.14 Using 声明
4.15 包索引
4.16 Friend 声明
4.17 折叠展开的约束
5 注释
6 示例
7 缺陷报告
8 参阅
[编辑] 语法
模板参数包(出现在别名模板、类模板、变量模板(C++14 起)、概念(C++20 起) 和函数模板参数列表中)
类型 ... 包名 (可选)
(1)
typename|class ... 包名 (可选)
(2)
类型约束 ... 包名 (可选)
(3)
(C++20 起)
template < 参数列表 > class ... 包名 (可选)
(4)
(C++17 前)
template < 参数列表 > typename|class ... 包名 (可选)
(4)
(C++17 起)
函数参数包(声明符的一种形式,出现在可变参数函数模板的函数参数列表中)
包名 ... 包参数名 (可选)
(5)
有关非参数包的语法,请参见lambda 初始化捕获包 和结构化绑定包(C++26 起)。
(C++20 起)
包展开(出现在模板的主体中)
模式 ...
(6)
1) 带有可选名称的非类型模板参数包
2) 带有可选名称的类型模板参数包
3) 带有可选名称的受约束类型模板参数包
(C++20 起)
4) 带有可选名称的模板模板参数包
5) 带有可选名称的函数参数包
6) 包展开:展开为零个或多个 pattern 列表。模式必须至少包含一个包。
[编辑] 解释
可变参数类模板可以使用任意数量的模板实参进行实例化
template
struct Tuple {};
Tuple<> t0; // Types contains no arguments
Tuple
Tuple
Tuple<0> t3; // error: 0 is not a type
可变参数函数模板可以使用任意数量的函数实参进行调用(模板实参通过模板实参推导推导出)
template
void f(Types... args);
f(); // OK: args contains no arguments
f(1); // OK: args contains one argument: int
f(2, 1.0); // OK: args contains two arguments: int and double
在主类模板中,模板参数包必须是模板参数列表中的最后一个参数。在函数模板中,模板参数包可以出现在列表中的更早位置,前提是所有后续参数都可以从函数实参中推导出来,或者具有默认实参
template
struct valid;
// template
// struct Invalid;
template
void valid(U, Ts...); // OK: can deduce U
// void valid(Ts..., U); // Can't be used: Ts... is a non-deduced context in this position
valid(1.0, 1, 2, 3); // OK: deduces U as double, Ts as {int, int, int}
如果可变参数模板的每个有效特化都需要一个空的模板参数包,则程序格式错误,不需要诊断。
[编辑] 包展开
在模式后跟省略号,其中至少一个包的名称至少出现一次,则会展开为模式的零个或多个实例化,其中包的名称按顺序被包中的每个元素替换。 对齐说明符的实例化以空格分隔,其他实例化以逗号分隔。
template
void f(Us... pargs) {}
template
void g(Ts... args)
{
f(&args...); // “&args...” is a pack expansion
// “&args” is its pattern
}
g(1, 0.2, "a"); // Ts... args expand to int E1, double E2, const char* E3
// &args... expands to &E1, &E2, &E3
// Us... pargs expand to int* E1, double* E2, const char** E3
如果两个包的名称出现在同一模式中,它们会同时展开,并且它们的长度必须相同
template
struct Tuple {};
template
struct Pair {};
template
struct zip
{
template
struct with
{
typedef Tuple
// Pair
// Pair
};
};
typedef zip
// Pair
// Pair
// T1 is Tuple
// typedef zip
// error: pack expansion contains packs of different lengths
如果一个包展开嵌套在另一个包展开中,则内部包展开中出现的包会由它展开,并且外部包展开中必须提及另一个包,但内部包展开中没有
template
void g(Args... args)
{
f(const_cast
// const_cast
// (Args and args) simultaneously
f(h(args...) + args...); // Nested pack expansion:
// inner pack expansion is "args...", it is expanded first
// outer pack expansion is h(E1, E2, E3) + args..., it is expanded
// second (as h(E1, E2, E3) + E1, h(E1, E2, E3) + E2, h(E1, E2, E3) + E3)
}
当包中的元素数量为零(空包)时,包展开的实例化不会改变外部构造的语法解释,即使在完全省略包展开会导致格式错误或导致语法歧义的情况下也是如此。实例化会生成一个空列表。
template
struct X : Bases... { };
template
void f(Args... args)
{
X
}
template void f<>(); // OK, X<> has no base classes
// x is a variable of type X<> that is value-initialized
[编辑] 展开位置
根据展开发生的位置,生成的逗号分隔(或对齐说明符的空格分隔)列表是不同类型的列表:函数参数列表、成员初始化器列表、属性列表等。以下是所有允许的上下文列表
[编辑] 函数实参列表
包展开可以出现在函数调用运算符的括号内,在这种情况下,省略号左侧的最大表达式或花括号括起来的初始化器列表是展开的模式
f(args...); // expands to f(E1, E2, E3)
f(&args...); // expands to f(&E1, &E2, &E3)
f(n, ++args...); // expands to f(n, ++E1, ++E2, ++E3);
f(++args..., n); // expands to f(++E1, ++E2, ++E3, n);
f(const_cast
// f(const_cast
f(h(args...) + args...); // expands to
// f(h(E1, E2, E3) + E1, h(E1, E2, E3) + E2, h(E1, E2, E3) + E3)
[编辑] 带括号的初始化器
包展开可以出现在直接初始化器、函数式转换和其他上下文(成员初始化器、new 表达式等)的括号内,在这种情况下,规则与上述函数调用表达式的规则相同
Class c1(&args...); // calls Class::Class(&E1, &E2, &E3)
Class c2 = Class(n, ++args...); // calls Class::Class(n, ++E1, ++E2, ++E3);
::new((void *)p) U(std::forward
[编辑] 花括号括起来的初始化器
在花括号括起来的初始化器列表中,也可以出现包展开
template
void func(Ts... args)
{
const int size = sizeof...(args) + 2;
int res[size] = {1, args..., 2};
// since initializer lists guarantee sequencing, this can be used to
// call a function on each element of a pack, in order:
int dummy[sizeof...(Ts)] = {(std::cout << args, 0)...};
}
[编辑] 模板实参列表
包展开可以在模板实参列表中的任何位置使用,前提是模板具有与展开匹配的参数
template
void func(A arg1, B arg2, C... arg3)
{
container t1; // expands to container
container
container t3; // expands to container
}
[编辑] 函数参数列表
在函数参数列表中,如果省略号出现在参数声明中(无论它是否命名函数参数包(如 Args... args)),则参数声明是模式
template
void f(Ts...) {}
f('a', 1); // Ts... expands to void f(char, int)
f(0.1); // Ts... expands to void f(double)
template
void g(Ts (&...arr)[N]) {}
int n[1];
g
// const char (&)[2], int(&)[1]
注意:在模式 Ts (&...arr)[N] 中,省略号是最内部的元素,而不是像所有其他包展开那样是最后一个元素。
注意:不允许 Ts (&...)[N],因为 C++11 语法要求带括号的省略号有一个名称:CWG issue 1488。
[编辑] 模板参数列表
包展开可以出现在模板参数列表中
template
struct value_holder
{
template
struct apply {}; // list, such as
};
[编辑] 基类说明符和成员初始化器列表
包展开可以指定类声明中的基类列表。通常,这也意味着构造函数需要在成员初始化器列表中使用包展开来调用这些基类的构造函数
template
class X : public Mixins...
{
public:
X(const Mixins&... mixins) : Mixins(mixins)... {}
};
[编辑] Lambda 捕获
包展开可以出现在lambda 表达式的捕获子句中
template
void f(Args... args)
{
auto lm = [&, args...] { return g(args...); };
lm();
}
[编辑] sizeof... 运算符
sizeof... 运算符也被归类为包展开
template
struct count
{
static const std::size_t value = sizeof...(Types);
};
动态异常规范
动态异常规范中的异常列表也可以是包展开
template
void func(int arg) throw(X...)
{
// ... throw different Xs in different situations
}
(C++17 前)
[编辑] 对齐说明符
包展开允许出现在关键字alignas使用的类型列表和表达式列表中。实例化以空格分隔
template
struct Align
{
alignas(T...) unsigned char buffer[128];
};
Align
// alignas(int) alignas(short)
// (no comma in between)
[编辑] 属性列表
如果属性规范允许,包展开允许出现在属性列表中。例如
template
[[vendor::attr(args)...]] void* f();
折叠表达式
在折叠表达式中,模式是整个不包含未展开包的子表达式。
Using 声明
在using 声明中,省略号可以出现在声明符列表中,这在从模板参数包派生时很有用
template
struct X : bases...
{
using bases::g...;
};
X x; // OK: B::g and D::g introduced
(C++17 起)
包索引
在包索引中,包展开包含一个未展开的包,后跟省略号和下标。包索引表达式的模式是标识符,而包索引说明符的模式是typedef-name。
consteval auto first_plus_last(auto... args)
{
return args...[0] + args...[sizeof...(args) - 1];
}
static_assert(first_plus_last(5) == 10);
static_assert(first_plus_last(5, 4) == 9);
static_assert(first_plus_last(5, 6, 2) == 7);
Friend 声明
在类friend 声明中,每个类型说明符后都可以跟一个省略号
struct C {};
struct E { struct Nested; };
template
class R
{
friend Ts...;
};
template
class R
{
friend Ts::Nested..., Us...;
};
R
R
折叠展开的约束
在折叠展开的约束中,模式是该折叠展开的约束的约束。
折叠展开的约束未实例化。
(C++26 起)
[编辑] 注意
本节不完整原因:关于部分特化和其他访问单个元素的方法的几句话?提及递归与对数与折叠表达式等快捷方式
特性测试宏
值
标准
特性
__cpp_variadic_templates
200704L
(C++11)
可变参数模板
__cpp_pack_indexing
202311L
(C++26)
包索引
[编辑] 示例
以下示例定义了一个类似于 std::printf 的函数,它将格式字符串中出现的每个字符 % 替换为一个值。
当只传递格式字符串且没有参数展开时,调用第一个重载。
第二个重载包含一个单独的模板参数用于参数的头部和一个参数包,这允许递归调用只传递参数的尾部,直到它变空。
Targs 是模板参数包,Fargs 是函数参数包。
运行此代码
#include
void tprintf(const char* format) // base function
{
std::cout << format;
}
template
void tprintf(const char* format, T value, Targs... Fargs) // recursive variadic function
{
for (; *format != '\0'; format++)
{
if (*format == '%')
{
std::cout << value;
tprintf(format + 1, Fargs...); // recursive call
return;
}
std::cout << *format;
}
}
int main()
{
tprintf("% world% %\n", "Hello", '!', 123);
}
输出
Hello world! 123
[编辑] 缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告
应用于
发布时的行为
正确的行为
CWG 1533
C++11
包展开可以出现在成员的成员初始化器中
不允许
CWG 2717
C++11
对齐说明符的实例化以逗号分隔
它们以空格分隔
[编辑] 另请参阅
函数模板
定义函数族
类模板
定义类族
sizeof...
查询包中的元素数量
C 风格可变参数函数
接受可变数量的参数
预处理器宏
也可以是可变参数的
折叠表达式
通过二元运算符约化一个包
包索引
访问包中指定索引处的元素