变长参数
允许函数接受任意数量的额外参数。
如果函数的参数列表的最后一个参数是省略号(...),则该函数是变参函数。
省略号之前的逗号可以省略。 | (C++26 中已弃用) |
// the function declared as follows int printx(const char* fmt, ...); int printx(const char* fmt...); // same as above, but deprecated since C++26 // may be called with one or more arguments: printx("hello world"); printx("a=%d b=%d", a, b); int printy(..., const char* fmt); // error: ... can only be the last parameter int printz(...); // valid, but the arguments cannot be accessed portably
这与函数参数包展开不同,参数包展开由作为参数声明符一部分的省略号指示,而不是单独的省略号参数。参数包展开和“变参”省略号都可以出现在函数模板的声明中,例如std::is_function。 |
(C++11 起) |
目录 |
[编辑] 默认参数提升
当调用变参函数时,在左值到右值、数组到指针以及函数到指针的转换之后,作为可变参数列表一部分的每个参数都会进行额外的转换,称为默认参数提升
|
(C++11 起) |
非 POD 类类型(C++11 前)作用域枚举和具有合格的非平凡复制构造函数、合格的非平凡移动构造函数或非平凡析构函数的类类型(C++11 起) 在潜在求值调用中是条件支持的,其语义由实现定义(这些类型在未求值调用中始终受支持)。
由于变参参数在重载决议中具有最低的等级,它们通常在 SFINAE 中用作包罗万象的备用方案。
在使用变参参数的函数体内,可以使用 <cstdarg>
库工具访问这些参数的值
定义于头文件
<cstdarg> | |
启用对变长函数参数的访问 (函数宏) | |
访问下一个变长函数参数 (函数宏) | |
(C++11) |
复制变长函数参数 (函数宏) |
结束变长函数参数的遍历 (函数宏) | |
保存 va_start、va_arg、va_end 和 va_copy 所需的信息 (类型定义) |
如果省略号之前的最后一个参数具有引用类型,或者其类型与默认参数提升后的类型不兼容,则 va_start 宏的行为是未定义的。
(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...); // 等同于 template<class... Ts> void f3(Ts...) |
(C++20 起) |
[编辑] 缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 发布时的行为 | 正确的行为 |
---|---|---|---|
CWG 506 | C++98 | 将非 POD 类参数传递给 省略号会导致未定义行为 |
传递此类参数是 条件支持的,且语义由 实现定义 |
CWG 634 | C++98 | 条件支持的类类型 导致某些 SFINAE 惯用法失效 |
如果未求值,则始终支持 |
CWG 2247 | C++11 | 没有限制将参数包 或 lambda 捕获传递给 va_start |
导致格式错误, 无需诊断 |
CWG 2347 | C++11 | 不清楚传递给省略号的 作用域枚举是否受默认参数提升影响 |
传递作用域枚举 是条件支持的,且语义由 实现定义 |
[编辑] 参阅
C 文档 关于 变参(Variadic arguments)
| |
C 文档 关于 隐式转换(Implicit conversions)
|