1.概述
可变参数模板(variadic template)是一个接收可变数目参数的模板函数或模板类。可变数目的参数被称为参数包(parameter packet)。存在两种参数包:模板参数包(template parameter packet),表示零个或多个模板参数;函数参数包(function parameter packet),表示零个或多个函数参数。
2.语法
用一个省略号指出一个模板参数或函数参数表示一个包。在一个模板参数列表中,class...
或typename...
指出接下来的参数表示零个或多个类型的列表;一个类型名后面跟一个省略号表示零个或多个给定类型的非类型参数的列表。在函数参数列表中,如果一个参数的类型是一个模板参数包,则此参数也是一个函数参数包。例如:
// Args是一个模板参数包;rest是一个函数参数包
// Args表示零个或多个模板类型参数
// rest表示零个或多个函数参数
template<typename T, typename... Args>
void foo(const T& t, const Args... rest);
上面声明了foo
是一个可变参数函数模板,它有一个名为T
的类型参数,和一个名为Args
的模板参数包。这个包表示零个或多个额外的类型参数。foo
的函数参数列表包含一个const&
类型的参数,指向T
的类型,还包含一个名为rest
的函数参数包,此包表示零个或多个函数参数。
与往常一样,编译器从函数的实参推断模板参数类型。对于一个可变参数模板,编译器还会推断包中的参数数量。例如,给定下面的调用:
int i = 0; double d = 3.14; string s = "how now brown cow";
foo(i, s, 42, d); // 包中有三个参数,i对应t,s 42 d属于参数包
foo(s, 42, "hi"); // 包中有两个参数
foo(d, s); // 包中有一个参数
foo("hi"); // 空包
编译器会为foo
实例化出四个不同的版本:
void foo(const int&, const string&, const int&, const double&);
void foo(const string&, const int&, const char[3]&);
void foo(const double&, const string&);
void foo(const char[3]&);
在每个实例中,T
的类型都是从第一个实参的类型中推断出来的,剩下的实参(如果有的话)提供函数额外实参的数目和类型。
当需要知道参数包中的参数数量时,可以使用sizeof...
运算符。类似sizeof
,sizeof...
也返回一个常量表达式,而且不会对其实参求值:
template<typename... Args>
void g(Args... args)
{
// 类型参数的数目
std::cout << sizeof...(Args) << std::endl;
// 函数参数的数目
std::cout << sizeof...(args) << std::endl;
}
3.实例
#include <iostream>
#include <bitset>
// 无参数时调用
void print()
{
std::cout << "\nprint end!" << std::endl;
}
// 递归调用,当参数为0时调用上面的print函数
template<typename T, typename... Types>
void print(const T& firstArg, const Types&... args)
{
std::cout << firstArg << ", ";
print(args...);
}
template<typename... Types>
void print_entry(const Types&... args)
{
// 打印参数类型数量和参数数量
std::cout << "Types size: " << sizeof...(Types)
<< ", args size: " << sizeof...(args) << std::endl;
print(args...);
}
int main(int argc, char* argv[])
{
print_entry(7.5, "hello", std::bitset<16>(377), 42);
return 0;
}
参考资料
- C++ Primer 第五版