其实可变参数是我们的老熟人了,我们平时使用的printf、scanf,它们的函数参数就是可变参数。

  • 以我们平时的使用很好理解,函数的可变参数就是可以有任意个的参数可以被函数接收,具体的形参参数由我们提供的实参来推演。
  • 可变参数的底层非常简单,它是用一个数组将实参的类型存储起来,然后输出到函数形参中。

C++11看中了可变参数的优势,随之可变参数模板应运而生……

一、基本语法及原理

C++11的新特性可变参数模板能够让我们创建可以接受可变参数的函数模板和类模板。

  • 相比C++98/03,类模版和函数模版中只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改进。
  • 然而由于可变模版参数比较抽象,使用起来需要一定的技巧,所以这块还是比较晦涩的。
  • 因此,本文我们学习一些基础的可变参数模板特性,这对我们日常使用足够了。

代码语言:javascript

AI代码解释

// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}

注意:

  • 上面的参数args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数包”。
  • 参数包里面包含了0到

N(N≥0)

个模版参数。

  • 参数包名字的是可以任意指定的,并不是说必须叫做Args和args。

现在,我们可以传入任意数量和任意类型的参数到ShowList中了

代码语言:javascript

AI代码解释

int main()
{
	ShowList();
	ShowList(1);
	ShowList(1, 1.1);
	ShowList(1, 1.1, 'a');
	ShowList(1, 1.1, 'a', string("woxiangchiyu"));
	
	return 0;
}

sizeof…运算符计算参数包中参数的个数

使用方式: sizeof...(参数包)

代码语言:javascript

AI代码解释

template <class ...Args>
void Print(Args&&... args)
{
	 cout << sizeof...(args) << endl;
}

int main()
{
	 double x = 2.2;
	 Print(); // 包⾥有0个参数 
	 Print(1); // 包⾥有1个参数 
	 Print(1, string("xxxxx")); // 包⾥有2个参数 
	 Print(1.1, string("xxxxx"), x); // 包⾥有3个参数 
	 
	 return 0;
}

二、展开参数包的方式

我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特点,也是最大的难点。

递归函数方式展开参数包

具体逻辑:

  1. 给函数模板添加一个模板参数,用于从接收到的函数包中分离出一个参数出来
  2. 递归调用自身函数,调用时只传参数包。
  3. 不断递归,每次都会分离出一个参数出来,直到参数包中没有参数

更多推荐