28. 可变参数函数
可变参数函数:参数数量可变,参数类型可变。比如 C 语言中的 printf
,参数数量是可变的,类型也是可变的。
28.1. C 语言的方式
C 语言是通过一个类型(va_list)和三个宏(va_start、va_arg、va_end)来实现可变参数的。
1#include <iostream>
2#include <cstdarg>
3using namespace std;
4
5void cPrint(int narg, ...)
6{
7 va_list args;
8 va_start(args, narg);
9 while(narg--)
10 {
11 cout << va_arg(args, int) << " ";
12 }
13 va_end(args);
14 cout << endl;
15}
16
17// 调用:cPrint(3, 5, 6, 23);
上例中 va_arg(args, int)
限定了解析的参数类型必须是整型,因而没有实现参数类型可变。 printf
是通过 %
来确定参数个数和类型的。
28.2. C++ 的方式
C++ 的可变参数模板得益于:
函数重载,依靠参数的 pattern 去匹配对应的函数;
函数模板,依靠调用时传递的参数自动推导出模板参数的类型;
类模板,基于偏特化(partial specialization)来选择不同的实现。
1#include <iostream>
2#include <cstdarg>
3using namespace std;
4
5// 递归出口
6// 当两个参数模板都适用某种情况时,优先使用没有 template parameter pack 的版本
7template<typename T>
8void cppPrint(T arg)
9{
10 cout << arg << endl;
11}
12
13template<typename T, typename... Ts> // template parameter pack,表明这里有多种 type
14void cppPrint(T arg1, Ts... args_left) // function parameter pack,表明这里有多个参数
15{
16 cout << arg1 << " ";
17 // 递归
18 cppPrint(args_left...); // pack expansion,将参数名字展开为逗号分割的参数列表
19}
20
21// 调用:cppPrint(5, 6.6, "foo");
C++ 通过重载 operator<<
来定制不同类型的输出。
Note
模板特化(template specialization)与偏特化(partial template specialization): 指定或限定全部/部分模板参数。
28.3. 参考资料
两种变长参数函数比较
C++的可变参数模板
模板特化与偏特化