命名空间
变体
操作

可变参数

来自 cppreference.com
< cpp‎ | 语言
 
 
C++语言
表达式
替代表示
字面量
布尔值 - 整数 - 浮点型
字符 - 字符串 - nullptr (C++11)
用户定义的 (C++11)
实用程序
属性 (C++11)
类型
typedef 声明
类型别名声明 (C++11)
强制转换
内存分配
特定于类的函数属性
explicit (C++11)
static

特殊成员函数
模板
其他
 
 

允许函数接受任意数量的额外参数。

由在 函数声明参数列表 之后出现的尾部 ... (除引入包扩展的省略号以外)(自 C++11 起) 表示。

参数列表 不为空时,一个可选的逗号可以在 ... 之前,表示一个可变参数函数。这提供了与 C 的兼容性(C 在从 C++ 采用函数原型时添加了对逗号的要求)。

// the function declared as follows
int printx(const char* fmt...);
// may be called with one or more arguments:
printx("hello world");
printx("a=%d b=%d", a, b);
 
int printx(const char* fmt, ...); // same as above (extraneous comma is allowed
                                  // for C compatibility)
int printy(..., const char* fmt); // error: ... cannot appear as a parameter
int printz(...); // valid, but the arguments cannot be accessed portably

注意:这不同于函数 参数包 展开,参数包展开由出现在参数声明符中的省略号表示,而不是出现在所有参数声明之后出现的省略号。参数包展开和“可变”省略号都可能出现在函数模板的声明中,如 std::is_function 的情况。

(自 C++11 起)

内容

[编辑] 默认转换

当调用可变参数函数时,在执行左值到右值、数组到指针和函数到指针 转换 后,作为可变参数列表一部分的每个参数都会经历额外的转换,称为默认参数提升

(自 C++11 起)
  • float 参数会转换为 double,就像在 浮点型提升 中一样。
  • boolcharshort 和无作用域枚举会转换为 int 或更宽的整数类型,就像在 整数提升 中一样。

非 POD 类类型(直到 C++11)作用域枚举和具有合格非平凡复制构造函数、合格非平凡移动构造函数或非平凡析构函数的类类型(自 C++11 起) 在可能求值的调用中具有实现定义的语义而有条件地支持(这些类型始终在 未求值的调用 中支持)。

由于可变参数在 重载解析 的目的中具有最低的等级,因此它们通常用作 SFINAE 中的兜底选项。

在使用可变参数的函数体中,可以使用 <cstdarg> 库设施 访问这些参数的值

定义在头文件 <cstdarg>
启用对可变参数函数参数的访问
(函数宏) [编辑]
访问下一个可变参数函数参数
(函数宏) [编辑]
(C++11)
创建可变参数函数参数的副本
(函数宏) [编辑]
结束对可变参数函数参数的遍历
(函数宏) [编辑]
保存 va_startva_argva_endva_copy 所需的信息
(typedef) [编辑]

如果省略号之前的最后一个参数是引用类型,或者其类型与默认参数提升后的类型不兼容,则va_start宏的行为是未定义的。

如果使用包展开lambda 捕获产生的实体作为va_start中的最后一个参数,则程序是错误的,无需诊断。

(自 C++11 起)

[编辑] 替代方法

  • 可变参数模板也可以用于创建接收可变数量参数的函数。它们通常是更好的选择,因为它们不会对参数的类型施加限制,不会执行整数和浮点数提升,并且类型安全。
  • 如果所有可变参数共享一个公共类型,std::initializer_list提供了一种便捷的机制(尽管语法不同)来访问可变参数。但是,在这种情况下,无法修改参数,因为std::initializer_list只能提供指向其元素的常量指针。
(自 C++11 起)

[编辑] 说明

在 C 编程语言中,直到 C23,至少需要在省略号参数之前出现一个命名参数,因此R printz(...);在 C23 之前是无效的。在 C++ 中,这种形式是允许的,即使传递给此类函数的参数不可访问,并且通常用作SFINAE中的回退重载,利用省略号转换在重载解析中的最低优先级。

这种可变参数语法是在 1983 年的 C++ 中引入的,省略号之前没有逗号。当 C89 从 C++ 中采用函数原型时,它用需要逗号的语法替换了该语法。为了兼容性,C++98 接受 C++ 风格的f(int n...)和 C 风格的f(int n, ...)

逗号可用于简化的函数模板,使省略号表示可变参数函数而不是可变参数模板

void f1(auto...);   // 与 template<class... Ts> void f3(Ts...) 相同
void f2(auto, ...); // 与 template<class T> void f3(T...) 相同

(自 C++20)

[编辑] 缺陷报告

以下行为更改缺陷报告已追溯应用于先前发布的 C++ 标准。

DR 应用于 发布的行为 正确的行为
CWG 506 C++98 将非 POD 类参数传递给
省略号导致未定义的行为
传递此类参数是
有条件支持的,具有
实现定义的语义
CWG 634 C++98 有条件支持的类类型
使某些 SFINAE 习惯用法无法正常工作
如果未评估,则始终支持
CWG 2247 C++11 对传递参数没有限制
包或 lambda 捕获到 va_start
使其无效,
无需诊断
CWG 2347 C++11 尚不清楚是否将作用域枚举传递给
省略号会受到默认参数提升的影响
传递作用域枚举
是条件支持的,具有
实现定义的语义

[编辑] 另请参阅

C 文档 for 可变参数