本文参考侯捷老师的视频:https://www.youtube.com/watch?v=TJIb9TGfDIw&list=PL-X74YXt4LVYo_bk-jHMV5T3LHRYRbZoH
以及C++ primer第五版 相关内容。
可变参数模板函数
//递归的终止条件
void print() {}
//Variadic Templates
//一般用于递归处理
template <typename T, typename... Types>
void print(const T& firstArg, const Types&... args) {cout << firstArg << endl;print(args...);
}
上面这个模板函数可以接收任意个数任意类型的参数并进行打印输出。其中typename... Types
是可变模板参数,表示可以接收任意类型,Types... args
表示可变函数参数,表示可以接收任意个数、任意类型的参数,要注意...
在不同的位置,这些都是固定的。可以看到我们输出第一个参数以后,调用了print(args...)
,如果args
中的参数个数大于0,则会继续调用模板函数,将第一个参数赋给firstArg
,其余参数赋给args
;如果参数个数等于0,则调用非模板函数void print()
结束递归。
可以看出,可变参数模板函数是通过递归来进行参数解包的。
特殊的,如果我们将上面的函数写成下面的版本:
//递归的终止条件
template<typename T>
void print(const T& t) {cout << t << endl;
}
//Variadic Templates
//一般用于递归处理
template<typename T, typename... Types>
void print(const T& t, const Types&... args) {print(t);print(args...);
}
同样完成将所有参数进行打印的工作。但是可能会有疑惑,对于print(t)
,似乎既可以调用void print(const T& t)
,又可以调用void print(const T& t, const Types... args)
,那么编译器会调用哪一个呢?这个是确定的,编译器会调用void print(const T& t)
。原因在于非可变参数版本比可变参数版本更加特例化。(详细模板重载规则见C++ primer 16.3 重载与模板)
在可变参数模板函数中,我们可以通过sizeof...(args)
获得可变参数的个数,返回一个常量表达式。
如果运行下面的代码,仍然会和上面的有同样的效果,void print(const Types... args)
不会运行:
//递归的终止条件
template<typename T>
void print(const T& t) {cout << t << endl;
}
//Variadic Templates
//一般用于递归处理
template<typename T, typename... Types>
void print(const T& firstArg, const Types&... args) {print(firstArg);print(args...);
}template<typename... Types>
void print(const Types&... args) {cout << sizeof...(args) << endl;
}
可变参数模板类
最经典的就是tuple
,通过递归地继承自己,实现了可以包含任意个数、任意类型的数据结构。