可变参数模板 Variadic Templates
从 C++11 开始,模板可以有任意个模板参数,也即所谓的可变参数模板。
可变参数模板
可变参数模板的例子
定义一个函数 print()
如下,用于接收不同类型的不同参数。
#include <iostream>
void print ()
{
}
template<typename T, typename... Types>
void print (T firstArg, Types... args)
{
std::cout << firstArg << '\n'; // print first argument
print(args...); // call print() for remaining arguments
}
typename... Args
被称为模板参数包(template parameter pack),Args... args
被称为函数参数包(function parameter pack)。模板函数 void print (T firstArg, Types... args)
首先打印第一个参数,然后递归调用自己打印剩余参数。为了结束递归,需要提供一个非模板类型重载函数,用于处理最后的空参数包。
为了更直观地感受可变参数模板地递归处理过程,可以在以上 print
中插入 __PRETTY_FUNCTION__
,打印出函数的调用。
#include <iostream>
void print ()
{
std::cout << __PRETTY_FUNCTION__ << "\n";
}
template<typename T, typename... Types>
void print (T firstArg, Types... args)
{
std::cout << firstArg << '\n'; // print first argument
std::cout << __PRETTY_FUNCTION__ << "\n";
print(args...); // call print() for remaining arguments
}
当发生 print (7.5, "hello", 5)
调用时,输出如下:
7.5
void print(T, Types ...) [with T = double; Types = {
const char*, int}]
hello
void print(T, Types ...) [with T = const char*; Types = {
int}]
5
void print(T, Types ...) [with T = int; Types = {
}]
void print()
重载可变和非可变参数模板
也可以实现上述例子如下:
#include <iostream>
template<typename T>
void print (T arg)
{
std::cout << arg << '\n'; // print passed argument
}
template<typename T, typename... Types>
void print (T firstArg, Types... args)
{
print(firstArg); // call print() for the first argument
print(args...); // call print() for remaining arguments
}
两个 ptint
的区别在于拖尾参数包,没有拖尾参数包的函数会被更优先匹配。
sizeof… 操作符
C++ 引入操作 sizeof...
用于计算可变参数包中元素的个数。
template<typename T, typename... Types>
void print (T firstArg, Types... args)
{
std::cout << sizeof...(Types) << '\n'; // print number of remaining types
std::cout << sizeof...(args) << '\n'; // print number of remaining args
...
}
添加以上两次 sizeof...
的调用,可以打印每次调用 print
时模板参数包和函数参数包元素的个数。
你可能会认为,借助 sizeof...
,可以省去一个空参数的 print
:
template<typename T, typename... Types>
void print (T firstArg, Types... args)
{
std::cout << firstArg << '\n';
if (sizeof...(args) > 0) {
// error if sizeof...(args)==0
print(args...); // and no print() for no arguments declared
}
}
但是,上面的代码无法工作。模板代码的实例是编译期决定的,而实例化的代码是否被执行是运行期决定的。也即 if
语句在编译期会生成,由于没有空参数的 print()
而导致编译失败。
在 C++17 开始支持编译期的 if
语句:
template<typename T, typename... Types>
void print