C++11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板,相比C++98/03,类模版和函数模版中只能含固定数量的模版参数。
C语言的可变函数参数和C++的可变参数模板有相似之处
C语言中,可变函数参数是通过stdarg.h头文件中的宏和函数来实现的。标准库中的printf、scanf,就是用可变函数参数的原理实现的。
va_list: 定义一个变量,用于存储可变参数的信息。va_start(va_list ap, last_arg): 初始化va_list,使其指向可变参数列表中的第一个参数。va_arg(va_list ap, type): 获取可变参数列表中的下一个参数的值,同时将va_list指向下一个参数。va_end(va_list ap): 清理va_list,使其无效。va_list逐个取出参数#include
#include
int sum(int count, ...)
{
int result = 0;
va_list args;
va_start(args, count);
for (int i = 0; i < count; ++i)
{
result += va_arg(args, int);
}
va_end(args);
return result;
}
int main()
{
int total = sum(3, 10, 20, 30);
printf("Sum: %d\n", total);
return 0;
}
C++中,可变模板参数是通过模板参数包来实现的。
Args ...args,这个参数包中可以包含0到任意个模板参数。sizeof...(Args),可以 获取模板参数包中的参数数量。下面是一个简单的可变参数的函数模板:
template <class ...Args>
void ShowList(Args... args)
{}
上面的参数args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数
包”,它里面包含了0到N(N>=0)个模版参数。我们无法直接获取参数包args中的每个参数的,
只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特
点,也是最大的难点,即如何展开可变模版参数。由于语法不支持使用args[i]这样方式获取可变
参数,所以我们的用一些奇招来一一获取参数包的值。
#include
// 递归展开模板参数包
// 递归终止函数
template<typename T>
void print(T value)
{
std::cout << value << std::endl;
}
// 展开函数
template<typename T, typename... Args>
void print(T first, Args... args)
{
std::cout << first << ", ";
print(args...);
}
int main()
{
print(1, 2.5, "Hello", 'a');
return 0;
}
如果参数包中各个参数的类型都是整型,那么也可以把这个参数包放到列表当中初始化这个整型数组,此时参数包中参数就放到数组中了:
//展开函数
template<class ...Args>
void ShowList(Args... args)
{
int arr[] = { args... }; //列表初始化
//打印参数包中的各个参数
for (auto e : arr)
{
cout << e << " ";
}
cout << endl;
}
int main()
{
ShowList(1, 2, 3, 4); //正确
ShowList("hello", 1, "world", 2); //错误:数组arr只能存放相同类型元素int
ShowList(); //错误:不能分配大小为0的数组
return 0;
}
虽然我们不能用不同类型的参数去初始化一个整型数组,但我们可以借助逗号表达式。
{(printarg(args), 0)...}将会展开成{(printarg(arg1),0),(printarg(arg2),0), (printarg(arg3),0), etc... }。比如:
//支持无参调用
void ShowList()
{
cout << endl;
}
//处理函数
template<class T>
void PrintArg(const T& t)
{
cout << t << " ";
}
//展开函数
template<class ...Args>
void ShowList(Args... args)
{
int arr[] = { (PrintArg(args), 0)... }; //列表初始化+逗号表达式
cout << endl;
}
stdarg.h来实现可变函数参数,通过va_list、va_start、va_arg和va_end宏进行操作。