c++11之可变模板参数

一、可变模板参数是什么?

可变模板参数是 C++11 中引入的一项特性,它允许你定义能够接受任意数量和类型的模板参数的模板。这个特性通过使用 (称为 “参数包”)来实现,可以在模板参数中使用它来表示零个或多个参数。

二、为什么使用可变模板参数?

可变模板参数是一个非常强大的特性,它极大地简化了模板编程,使得可以编写更加通用和灵活的代码。它常用于实现元编程技术、泛型编程、以及创建能够接受任意数量和类型参数的函数和类。

三、具体使用

可变参数模板语法

template <class... T>
void f(T... args);

上面的可变模版参数的定义当中,省略号的作用有两个:

  1. 声明一个参数包T… args,这个参数包中可以包含0到任意个模板参数;
  2. 在模板定义的右边,可以将参数包展开成一个一个独立的参数。

可变模版参数的展开

例子:

#include <iostream>
using namespace std;
template <class... T>
void f(T... args)
{
cout << sizeof...(args) << endl; //打印变参的个数
}
int main()
{
f(); //0
f(1, 2); //2
f(1, 2.5, ""); //3
return 0;
}

展开可变模版参数函数的方法一般有两种

  1. 通过递归函数来展开参数包。
  2. 是通过逗号表达式来展开参数包。

递归函数来展开参数包

#include <iostream>
using namespace std;
//递归终止函数
void print()
{
	cout << "empty" << endl;
}
//展开函数
template <class T, class ...Args>
void print(T head, Args... rest)
{
	cout << "parameter " << head << endl;
	print(rest...);	
}
int main(void)
{
	print(1,2,3,4);
	return 0;
}

代码标注:
一般需要两个函数,一个是展开函数,一个是递归终止函数。如上面例子,第一次调用函数时,=
head = 1, 然后Args… rest 包含2,3,4,然后第二次递归中, head = 2 , Args… rest 包含3,4, … 最后直到Args… res 为空,就直接调用void print() 这个终止函数,完成。
或者你可以递归到最后一个参数就终止,而不递归到参数为空,如下:

//递归终止函数
template<class T>
void print(T x)
{
	cout << "parameter" << x << endl;
}

逗号表达式展开参数包

递归函数展开参数包是一种标准做法,也比较好理解,但也有一个缺点,就是必须要一个重载的递归终止函数,即必须要有一个同名的终止函数来终止递归,这样可能会感觉稍有不便。有没有一种更简单的方式呢?其实还有一种方法可以不通过递归方式来展开参数包,这种方式需要借助逗号表达式和初始化列表。比如前面print的例子可以改成这样:

#include <iostream>
using namespace std;
template <class T>
void printarg(T t)
{
cout << t << endl;
}
template <class ...Args>
void expand(Args... args)
{
	int arr[] = {(printarg(args), 0)...};
}
int main()
{
	expand(1,2,3,4);
	return 0;
}

这个例子将分别打印出1,2,3,4四个数字。这种展开参数包的方式,不需要通过递归终止函数,是直接在expand函数体中展开的, printarg不是一个递归终止函数,只是一个处理参数包中每一个参数的函数。
expand函数中的逗号表达式:(printarg(args), 0),先执行printarg(args),再得到逗号表达式的结果0。
同时还用到了C++11的另外一个特性——初始化列表,通过初始化列表来初始化一个变长数组,
{(printarg(args), 0)…}将会展开成((printarg(arg1),0), (printarg(arg2),0), (printarg(arg3),0), etc… ),最
终会创建一个元素值都为0的数组int arr[sizeof…(Args)]。由于是逗号表达式,在创建数组的过程中会先执行逗号表达式前面的部分printarg(args)打印出参数,也就是说在构造int数组的过程中就将参数包展开了,这个数组的目的纯粹是为了在数组构造的过程展开参数包。

我们可以把上面的例子再进一步改进一下,将函数作为参数,就可以支持lambda表达式了,从而可以少写一个递归终止函数了,具体代码如下:

#include <iostream>
using namespace std;
template<class F, class... Args>
void expand(const F& f, Args&&...args)
{
	//这里用到了完美转发
	initializer_list<int>{(f(std::forward< Args>(args)),0)...};
}
int main()
{
expand([](int i){cout<<i<<endl;}, 1,2,3);
return 0;
}
	都看到这里了,还不来个免费的点赞,然后关注一手~
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值