命名空间
变体
操作

可变参数

来自 cppreference.cn
< cpp‎ | language
 
 
C++ 语言
 
 

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

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

省略号前面的逗号可以省略。 (在 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_start、va_arg、va_end 和 va_copy 所需的信息
(typedef) [编辑]

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

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

(始于 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++26 起,原始的 C++ 风格语法已被弃用。

逗号可以在缩写函数模板中使用,使省略号表示可变参数函数而不是可变参数模板

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 文档关于 可变参数
C 文档关于 隐式转换