C++ 可变参数模板

可变参数模板是C++11引入的特性,它能够创建可变数量参数的模板函数和模板类。这一特性让我们可以编写出更加灵活和通用的代码。

可能光这样子说,没有感觉,那就举一个简单的例子吧,假设现在有一个需求:

要编写一个函数,它可以接收任意数量的参数,并且参数类型只需要是cout能够显示的即可。 

从需求来看,传进函数的参数数量不固定,并且类型的顺序也不固定,我们不可能定义很多个打印函数来做到这个需求,因为组合的情况太多太多了。

这种情况下,用可变参数模板就可以解决这个问题。在后面的部分,我将用上面这个例子贯穿全文来说明可变参数模板,旨在说明可变参数模板这个技术。

一. 模板参数包和函数参数包

       C++11提供了一个用省略号(...)表示的元运算符,用这个运算符来表示模板参数包和函数参数包的标识符,模板参数包基本上是一个类型列表,函数参数包基本上是一个值列表,语法如下:

                       

       模板参数包Args与任意数量(包括零)的类型匹配,函数参数包args所包含的值列表与模板参数包所含的类型列表(无论是类型还是数量)匹配。

上面那个打印函数参数的那个例子,我们可以声明为如下这样:

template<typename... Args>
void show_list(Args... args) {

 ......

}

如果现在我们需要这样调用它:

show_list('I', "love", 2024);
  • 模板参数包Args包含的类型就是:char、const char*、int。
  • 函数参数包args对应包含的值就是:'I'、 "love"、2024。

二.展开参数包

那模板参数包和函数参数包的内容该如何访问呢?索引功能在这里无法使用,即不能使用类似于Args[1]来访问包中的第二个参数类型;

1. 递归方式访问

函数参数包的话,可以将省略号放在其右边(即:args...),将参数包展开。采用递归的方式访问:对参数列表里的第一项进行处理,再将余下的内容传递给递归调用,直到列表为空。 

还是关于打印参数这个例子,看一下如何展开这个函数参数包并打印。和常规递归一样,确保递归终止很重要, 最后写出来的代码如下:

// 递归终止条件
void show_list() {};

template<typename T, typename... Args>
void show_list(T value, Args... args) {
   // 打印第一个参数,然后递归打印剩余参数
	cout << value << " ";
	show_list(args...);
};

void test01() {
	show_list('I', "love", 2024);
	return;
};

在上面的示例中,show_list函数使用了可变参数模板。当调用show_list函数时,它会递归地打印每个参数,直到没有剩余参数为止。 这里有一个技巧就是函数模板的头改为了:

template<typename T, typename... Args>
void show_list(T value, Args... args) {}

 show_list()的第一个实参决定了T和value的值,而其他实参决定了Args和args的值。这让函数能够对value进行处理;然后递归调用show_list(),并以args...的方式将其他实参传递给它。每一次递归调用处理一个值,并且传递缩短了的列表,直到列表为空为止:

  1. 当调用了show_list('I', "love", 2024),第一个实参会使得T为char,value为‘I’;另外两个类型(string和int)将放入Args包中,对应的另外两个实参将放入args包中。
  2. 接下来show_list(args...):  考虑到args...的展开作用,这一步与如下代码等价:show_list("love", 2024)。至此参数列表减少了一项
  3. 再按上述规则依次类推,直到参数列表为空为止。

最后函数调用过程就如下面这张图所示

2. 折叠表达式展开 

折叠表达式展开的方式是到了C++ 17才提供的,C++ 11是不支援的。折叠表达式示例如下:

template<typename... Args>
void show_list(const Args&... args) {
	(cout << ... << args)<<endl;
};

 但是打印时各个参数都粘在了一起,不好看。可以利用逗号表达式控制一下格式打印,如下所示:

template<typename... Args>
void show_list(const Args&... args) {
	(cout << ... << (cout<<args," ")) << endl;
};

3. 其他方式

还有其他的展开参数包的方式,不过这里就不多做介绍了,可以参考其他的博客:

 c++可变参数模板展开参数包_c++ 模板形参包的展开-CSDN博客

  • 10
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值