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. 参考资料

  1. 两种变长参数函数比较

  1. C++的可变参数模板

  1. 模板特化与偏特化