变长实参

来自cppreference.com
< cpp‎ | language


 
 
 
 

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

变参函数的形参列表的最后一个形参是省略号(...)。

位于省略号之前的逗号可以省略。(C++26 弃用)
// 函数声明如下int printx(constchar* fmt, ...);int printx(constchar* fmt...);// 同上,但 C++26 起被弃用   // 能以一个或多个实参调用: printx("hello world"); printx("a=%d b=%d", a, b);   int printy(..., constchar* fmt);// 错误:... 只能是最后一个形参int printz(...);// 合法,但无法以可移植的方式访问实参

这与函数形参包展开不同,形参包展开是由作为形参声明符一部分的省略号指定的,而非以省略号表示整个形参。形参包展开和“变参”省略号都能出现在函数模板声明中,如 std::is_function 的情况。

(C++11 起)

目录

[编辑]默认实参提升

调用变参函数时,进行左值到右值、数组到指针及函数到指针的转换后,可变实参列表中的每个实参都要经过称为默认实参提升 的额外转换:

(C++11 起)
  • 转换 floatdouble,如同浮点数提升
  • 转换 boolcharshort无作用域(C++11 起)枚举到 int 或更宽的整数类型,如同整数提升

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

因为变长形参对于重载决议而言具有最低的优先级,所以它们常被用作 SFINAE 中的万应后备。

在使用变长实参的函数体内,可以用一些 <cstdarg> 库设施访问这些实参的值:

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

如果省略号前的最后一个形参具有引用类型,或者它的类型与默认实参提升所产生的各种类型不兼容,那么 va_start 宏的行为未定义。

如果在 va_start 中以包展开或源自 lambda 捕获的实体为最后的形参,那么程序非良构,不要求诊断。

(C++11 起)

[编辑]替代方案

  • 变参模板也可以用来创建接受可变数量实参的函数。它们通常是更好的选择,因为它们对实参类型不施加任何制约,不进行整数和浮点数提升,且类型安全。
  • 当所有变长实参均为相同类型时,std::initializer_list 提供了一种访问变长实参的便利机制。不过这种情况下不能修改各个实参,因为 std::initializer_list 仅提供指向它的各个元素的 const 指针。
(C++11 起)

[编辑]注解

因为在 C23 前的 C 编程语言中,省略号前必须至少出现一个具名形参,所以 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 文档
隐式转换C 文档
close