命名空间
变体
操作

变长参数

来自 cppreference.cn
< cpp‎ | 语言
 
 
C++ 语言
表达式
替代表示
字面量
布尔(Boolean) - 整型(Integer) - 浮点型(Floating-point)
字符(Character) - 字符串(String) - nullptr (C++11)
用户定义 (C++11)
工具
属性 (C++11)
类型
typedef 声明
类型别名声明 (C++11)
类型转换
内存分配
类特有的函数属性
explicit (C++11)
static

特殊成员函数
模板
杂项
 
 

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

如果函数的参数列表的最后一个参数是省略号(...),则该函数是变参函数。

省略号之前的逗号可以省略。 (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 起)
  • float 参数被转换为 double,如浮点提升
  • boolcharshort 和无作用域枚举被转换为 int 或更宽的整数类型,如整型提升

非 POD 类类型(C++11 前)作用域枚举和具有合格的非平凡复制构造函数、合格的非平凡移动构造函数或非平凡析构函数的类类型(C++11 起) 在潜在求值调用中是条件支持的,其语义由实现定义(这些类型在未求值调用中始终受支持)。

由于变参参数在重载决议中具有最低的等级,它们通常在 SFINAE 中用作包罗万象的备用方案。

在使用变参参数的函数体内,可以使用 <cstdarg> 库工具访问这些参数的值

定义于头文件 <cstdarg>
启用对变长函数参数的访问
(函数宏) [编辑]
访问下一个变长函数参数
(函数宏) [编辑]
(C++11)
复制变长函数参数
(函数宏) [编辑]
结束变长函数参数的遍历
(函数宏) [编辑]
保存 va_startva_argva_endva_copy 所需的信息
(类型定义) [编辑]

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

如果将包展开或由lambda 捕获产生的实体用作 va_start 的最后一个参数,则程序格式错误,无需诊断。

(C++11 起)

[编辑] 替代方案

  • 变参模板也可以用来创建接受可变数量参数的函数。它们通常是更好的选择,因为它们不对参数的类型施加限制,不执行整数和浮点提升,并且是类型安全的。
  • 如果所有可变参数都具有共同的类型,则 std::initializer_list 提供了一种方便的机制(尽管语法不同)来访问可变参数。但是,在这种情况下,参数不能被修改,因为 std::initializer_list 只能提供一个指向其元素的 const 指针。
(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...)
void f2(auto, ...); // 等同于 template<class T> void f3(T, ...)

(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)