学科分类
目录
C++基础

可变参数模板

在C++11之前,不论是类模板或是函数模板都只允许接收固定数目的模板参数。C++11新增加了可变参数模板,其中允许使用任意个数、任意类型的模板参数,无需在模板定义时将参数固定。可变数目的参数称为参数包,在模板中通过省略号(“…”)表示。参数包可分为两种:模板参数包,表示零个或多个模板参数,函数参数包,表示零个或多个函数参数。

接下来给出一个可变参数函数模板,学习其定义方式,代码如下所示:

template <class T, class... Args>      //Args是模板参数包,表示零个或多个模板参数
void foo(const T& t, const Args&... args) //args是函数参数包,表示零个或多个函数参数
{
  函数体
}

上述代码中,定义可变参数函数模板foo(),该函数拥有一个T类型引用参数,以及一个名args的模板参数包,包中可以包含零个或多个类型参数。

与普通的函数模板一样,编译器根据函数实例,推断出模板参数类型,对于可变参数模板函数还需要推断出可变参数个数。根据上述foo()函数模板,若有如下函数调用形式,编译器根据实际参数类型及数量可以推断出foo()函数的不同版本,代码如下所示:

int n = 0;
double d = 3.14;
string s = “hello”;
foo(n, s, 10, d);               //参数包中有三个参数
foo(s, 10);                   //参数包中有一个参数
foo(n);                     //参数包中无参数

根据上述代码,编译器为foo()函数实例化出三个版本,内容如下所示:

void foo(const int &, const string &, const int &, const double &);
void foo(const string &, const int &);
void foo(const int &);

根据调用形式中第一个参数推断出模板中T的类型,剩余参数提供可变参数内容。

可变参数模板还可以应用在模板类中,若有如下声明:

template<classValues> 
class Tuple;

模板类Tuple的对象能接收多个不同类型参数作为它的模板形参,若有如下代码:

class Tuple<int,std::vector<int>,std::map<std::string,std::vector<int>>> obj;

上述Tuple类的参数包中包含int、vector、map三种类型参数。

在传统的C语言中,printf ()函数是常用的可变参函数,能够对不同类型的任意多个数据,按格式进行输出,C++中可以通过可变参数模板实现printf()的功能。变长参数模板中无法像在类或函数中那样使用参数包,在这里以递归的方法取出可用参数,下面是利用C++11中可变参数模板实现的printf()函数,代码如下所示:

//定义只有一个字符指针参数的函数, 其调用形式如printf(“hello”)
void printf(const char *s)
{
  while (*s)
  {
​    //若出现形如printf(“%d,hello!”)的调用形式,抛出异常//因为此时printf()函数实现的是将参数字符串原样输出,不会处理格式控制符if (*s == '%' && *(++s) != '%')
​      throw std::runtime_error("invalid format string: missing arguments");
​    std::cout << *s++;             //输出字符内容
  }
}
//定义普通的printf()函数模板,用于输出多种不同类型数据
template<typename T, typename... Args>
void printf(const char* s, T value, Args... args)
{
  while (*s)
  {
​    if (*s == '%' && *(++s) != '%')
​    {
​      std::cout << value;
//递归调用printf(),每次从参数包中取出一个参数
​      printf(*s ? ++s : s, args...); 
​      return;
​    }
​    std::cout << *s++;
  }
  throw std::logic_error("extra arguments provided to printf");
}

printf()函数会不断地递归调用自身:函数参数包 args... 在调用时,会被模板类匹配分离为T value和 Args... args直到 args... 变为空参数,则会与简单的 printf(const char *s) 匹配,退出递归。

点击此处
隐藏目录