可变参数模板
一个可变参数模板就是一个接受可变数目参数的模板函数或模板类,可变数目的参数被称为参数包,存在两种参数包:模板参数包,表示零个或多个模板参数;函数参数包,表示零个或多个函数参数
template<typename T, typename... Args>
void foo(const T& t, const Args&... rest) {
cout << sizeof...(rest) << endl;
}
// 打印参数包的参数个数
int i = 0;
double d = 3.14;
string s = "hello";
foo(i, s, 42, d); // 3
foo(s, 42, "hi"); // 2
foo(d, s); // 1
foo("hi"); // 0
编译器会实例化出4个不同的版本
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]&);
16.4.1 编写可变参数的函数模板
// 用来终止递归并打印最后一个元素
// 此函数必须在可变参数版本的print定义之前声明
template<typename T>
ostream& print(ostream& os, const T& t) {
return os << t;
}
template<typename T, typename... Args>
ostream& print(ostream& os, const T& t, const Args... rest) {
os << t << ", ";
return print(os, rest...);
}
int i = 0;
string s = "hello";
print(cout, i, s, 42);
一开始调用print(cout, i, s, 42)
输出i
然后调用print(cout, s, 42)
输出s
再调用print(cout, 42)
,此时两个版本都可以,但是非可变参数模板比可变参数模板更特例化,因此调用非可变参数版本,结束递归
16.4.2 包扩展
print函数包含两个扩展
template<typename T, typename... Args>
ostream& print(ostream& os, const T& t, const Args... rest) { // 扩展Args
os << t << ", ";
return print(os, rest...); // 扩展rest
}
第一个扩展操作扩展模板参数包,为print
生成函数参数列表。第二个扩展操作出现在对print
的调用中,此模式为print
调用生成实参列表
递归调用的时候也可以对每个实参调用函数
template<typename T, typename... Args>
ostream& print(ostream& os, const T& t, const Args... rest) { // 扩展Args
os << t << ", ";
return print(os, debug_rep(rest)...); // 扩展rest
}
相当于
print(os, debug_rep(a1), debug_rep(a2), ..., debug_rep(an));
不能写成
print(os, debug_rep(rest...));
这相当于
print(os, debug_rep(a1, a2, ..., an));
16.4.3 转发扩展包
可变参数函数通常将它们的参数转发给其他函数
// fun有零个或多个参数,每个参数都是一个模板参数类型的右值引用
template<typename... Args>
void fun(Args&&... args) { // 将Args扩展为一个右值引用的列表
// work的实参既扩展Args,又扩展args
work(std::forward<Args>(args)...);
}
int a = 10;
fun(a, 3.4); // 可以接收左值,也可以接收右值
这里我们希望将fun
的所有实参转发给另一个名为work
的函数,假定由它完成函数的实际工作。work
调用中的扩展既扩展了模板参数包也扩展了函数参数包
由于fun
的参数是右值引用,因此我们可以传递给它任意类型的实参,由于使用std::forward
传递这些实参,因此它们的所有类型信息在调用work
时都会得到保持