C++可变参数的函数与模板实例分析

2020-01-06 12:51:26于丽

一个可变参数模板(variadic template)就是一个接受可变数目参数的模板函数或模板类。可变数目的参数被称为参数包(parameter packet)。存在两种参数包:模板参数包(表示零个或多个模板参数)和函数参数包(表示零个或多个函数参数)。

上述说到我们可以使用一个initializer_list来定义一个可接受可变数目实参的函数,但是所有实参必须具有相同的类型。当我们既不知道要处理的实参数目也不知道它们的类型时,我们就需要使用可变参数的函数模板了。我们用一个省略号来指出一个模板参数或函数参数表示一个包:在一个模板参数列表中,class...或typename...指出接下来的参数表示零个或多个类型的列表;一个类型名后面跟一个省略号表示零个或多个给定类型的非类型参数的列表。在函数参数列表中,如果一个参数的类型是一个模板参数包,则此参数也是一个函数参数包。


// Args是一个模板参数包;rest是一个函数参数包 
template <typename T, typename...Args> 
void foo(const T &t, const Args&...rest); 

可变参数函数模板通常是递归的。第一步调用处理包中的第一个实参,然后用剩余的实参调用自身。为了终止递归,我们还需要定义一个非可变参数的函数模板:


// 用来终止递归并处理包中最后一个元素 
template <typename T> 
void print(const T &t) 
{ 
  cout << t; 
} 
 
// 包中除了最后一个元素之外的其他元素都会调用这个版本的print 
template <typename T, typename...Args> 
void print(const T &t, const Args&...rest) 
{ 
  cout << t << " ";   // 打印第一个实参 
  print(rest...);    // 递归调用,打印其他实参 
} 
 
// 测试 
int main() 
{ 
  print("string1", 2, 3.14f, "string2", 42); 
  cout << endl; 
  return 0; 
} 

非可变参数版本的print负责终止递归并打印初始调用中的最后一个实参。对于最后一次递归调用print(42),两个print版本都是可行的。但是,非可变参数模板比可变参数模板更特例化,因此编译器选择非可变参数版本。