Variadic arguments
允许函数接受任意数量的额外参数。
当函数的 参数列表 中最后一个参数是省略号( ... )时,该函数为可变参数函数。
| 省略号前的逗号可以省略。 | (deprecated in C++26) |
// 函数声明如下 int printx(const char* fmt, ...); int printx(const char* fmt...); // 与上述相同,但自 C++26 起已弃用 // 可使用一个或多个参数调用: printx("hello world"); printx("a=%d b=%d", a, b); int printy(..., const char* fmt); // 错误:... 只能是最后一个参数 int printz(...); // 有效,但无法以可移植方式访问参数
|
这与函数 形参包 展开不同——后者由作为形参声明符组成部分的省略号表示,而非将省略号作为独立形参。形参包展开和“可变参数”省略号均可出现在函数模板声明中,例如 std::is_function 的情况。 |
(since C++11) |
目录 |
默认参数提升
当调用可变参数函数时,在经历左值到右值、数组到指针和函数到指针 转换 后,属于可变参数列表的每个参数还会进行额外的转换,称为 默认参数提升 :
|
(since C++11) |
非POD类类型 (C++11前) 具有合格非平凡复制构造函数、合格非平凡移动构造函数或非平凡析构函数的作用域枚举和类类型 (C++11起) 在具有实现定义语义的潜在求值调用中为有条件支持(这些类型在 不求值调用 中始终受支持)。
由于可变参数在 重载解析 中具有最低优先级,它们通常被用作 SFINAE 中的全能后备方案。
在使用可变参数的函数体内,可以通过
<cstdarg>
库工具
访问这些参数的值:
|
定义于头文件
<cstdarg>
|
|
|
启用可变参数函数参数的访问
(函数宏) |
|
|
访问下一个可变参数函数参数
(函数宏) |
|
|
(C++11)
|
创建可变参数函数参数的副本
(函数宏) |
|
结束可变参数函数参数的遍历
(函数宏) |
|
|
持有
va_start
、
va_arg
、
va_end
和
va_copy
所需的信息
(类型定义) |
|
若省略号前的最后一个参数具有引用类型,或其类型与 默认参数提升 后的类型不兼容,则 va_start 宏的行为是未定义的。
| (since C++11) |
替代方案
|
(自 C++11 起) |
注释
在C语言中,直到C23标准之前,省略号参数前必须至少出现一个命名参数,因此 R printz ( ... ) ; 在C23之前是无效的。在C++中,即使传递给此类函数的参数不可访问,这种形式也是被允许的,并且通常用作 SFINAE 中的备选重载,利用省略号转换在 重载解析 中的最低优先级特性。
这种可变参数语法于1983年在C++中引入时,省略号前没有逗号。当C89从C++引入函数原型时,将其替换为要求逗号的语法。为保持兼容性,C++98同时接受C++风格的 f ( int n... ) 和C风格的 f ( int n, ... ) 。原始C++风格语法自C++26起被弃用。
|
逗号可用于简写函数模板中,使省略号表示可变参数函数而非可变参数模板:
void
f1
(
auto
...
)
;
// same as template<class... Ts> void f3(Ts...)
|
(since C++20) |
缺陷报告
以下行为变更缺陷报告被追溯应用于先前发布的C++标准。
| 缺陷报告 | 适用标准 | 发布时行为 | 正确行为 |
|---|---|---|---|
| CWG 506 | C++98 |
向省略号传递非POD类参数
会导致未定义行为 |
传递此类参数为有条件支持,
具有实现定义的语义 |
| CWG 634 | C++98 |
有条件支持的类类型
导致某些SFINAE用法失效 |
在未求值语境中始终支持 |
| CWG 2247 | C++11 |
未限制向
va_start
传递
参数包或lambda捕获 |
视为病式构造,
不要求诊断 |
| CWG 2347 | C++11 |
未明确作用域枚举类型传递给
省略号时是否适用默认参数提升 |
传递作用域枚举为有条件支持,
具有实现定义的语义 |
参阅
|
C 文档
关于
可变参数
|
|
|
C 文档
关于
隐式转换
|