c++11增加的变参数模板,今天总算整明白了

本篇文章介绍一下c++11中增加的变参数模板template<typename... _Args>到底是咋回事,以及它的具体用法。

说明一下,我用的是gcc7.1.0编译器,标准库源代码也是这个版本的。

按照惯例,还是先看一下本文大纲,如下:

在这里插入图片描述

在之前写vector和deque容器源码剖析的过程中,经常发现这样的代码,如下:

template<typename... _Args>
void emplace_front(_Args&&... __args);

可以看到里面模板参数是template<typename... _Args>,其实这个就是变参数模板,然后它的参数也是比较特别的_Args&&... __args,去除右值引用的话,它就是一个可变参数,那么可变参数模板和可变参数到底是什么,应该怎么使用呢,我们今天就来深究一下这些事情。

1. 什么是变参数模板

c++11中新增加了一项内容,叫做变参数模板,所谓变参数模板,顾名思义就是参数个数和类型都可能发生变化的模板,要实现这一点,那就必须要使用模板形参包。

模板形参包是可以接受0个或者n个模板实参的模板形参,至少有一个模板形参包的模板就可以称作变参数模板,所以说白了,搞懂了模板形参包就明白变参数模板了,因为变参数模板就是基于模板形参包来实现的,接下来我们就来看看到底啥是模板形参包。

2. 变参数模板的基础-模板形参包

模板形参包主要出现在函数模板和类模板中,目前来讲,模板形参包主要有三种,即:非类型模板形参包、类型模板形参包、模板模板形参包。

2.1 非类型模板形参包

非类型模板形参包语法是这样的:

template<类型 ... args>

初看会很疑惑,说是非类型模板形参包,怎么语法里面一开始就是一个类型的,其实这里的非类型是针对typenameclass关键字来的,都知道模板使用typename或者class关键字表示它们后面跟着的名称是类型名称,而这里的形参包里面类型其实表示一个固定的类型,所以这里其实不如叫做固定类型模板形参包。

对于上述非类型模板形参包而言,类型选择一个固定的类型,args其实是一个可修改的参数名,如下:

template<int ... data> xxxxxx;

注意,这个固定的类型是有限制的,标准c++规定,只能为整型、指针和引用。

但是这个形参包该怎么用呢,有这样一个例子,比如我想统计这个幼儿园的小朋友们的年龄总和,但是目前并不知道总共有多少个小朋友,那么此时就可以用这个非类型模板形参包,代码如下:

#include <iostream>
using namespace std;

//这里加一个空模板函数是为了编译可以通过,否则编译期间调用printAmt<int>(int&)就会找不到可匹配的函数
//模板参数第一个类型实际上是用不到的,但是这里必须要加上,否则就是调用printAmt<>(int&),模板实参为空,但是模板形参列表是不能为空的
template<class type>
void printAmt(int &iSumAge)
{
   
	return;
}

template<class type, int age0, int ... age>
void printAmt(int &iSumAge)
{
   
	iSumAge += age0;
    //这里sizeof ... (age)是计算形参包里的形参个数,返回类型是std::size_t,后续同理
	if ( (sizeof ... (age)) > 0 )
	{
   
        //这里的age...其实就是语法中的一种包展开,这个后续会具体说明
		printAmt<type, age...>(iSumAge);
	}
}

int main()
{
   
	int sumAge = 0;
	printAmt<int,1,2,3,4,5,7,6,8>(sumAge);
	cout << "the sum of age is " << sumAge << endl;
	return 0;
}

这里只是以此为例来说明一下非类型模板形参包的使用,实际项目中这么简单的事肯定是没有必要还写个模板的。

根据语法和代码的使用情况,我们对非类型模板形参包总结如下:

  • 非类型模板形参包类型是固定的,但参数名跟普通函数参数一样,是可以修改的;
  • 传递给非类型模板形参包的实参不是类型,而是实际的值。
2.2 类型模板形参包

类型模板形参包语法如下:

typename|class ... Args

这个就是很正常的模板形参了哈,typename关键字和class关键字都可以用于在模板中声明一个未知类型,只是在以前template<typename type>的基础上加了一个省略号,改成了可变形参包而已,该可变形参包可以接受无限个不同的实参类型。

现在我们先用一下这个类型模板形参包看看,假设我们有这样一种场景,我想输出一个人的姓名、性别、年龄、身高等个人信息,但是具体有哪些信息我们不能确定,那应该怎么办呢?

分析一下,具体信息不固定,类型也不固定,此时就可以使用类型模板形参包了,看下面这段代码:

#include <iostream>
using std::cout;
using std::endl;

void xprintf()
{
   
    cout << endl;
}

template<typename T, typename... Targs>
void xprintf(T value, Targs... Fargs)
{
   
	cout << value << ' ';
	if ( (sizeof ...(Fargs)) > 0 )
	{
   
        //这里调用的时候没有显式指定模板,是因为函数模板可以根据函数参数自动推导
		xprintf(Fargs...);
	}
	else
	{
   
		xprintf();
	}
}
 
int main()
{
   
    xprintf("小明个人信息:", "小明", "男", 35, "程序员", 169.5);
    return 0;
}

输出结果如下:

小明个人信息: 小明 男 35 程序员 169.5

这个就是一个类型模板形参包在函数模板里面的典型使用,可以看到,

当然啦,有人会说了,其实cout一行代码就可以搞定,但是我们这里是提供通用型接口,具体要输出哪些信息事先并不知道,这个时候使用类型模板形参包就很方便啦。

2.3 模板模板形参包

这个就有点绕了,模板模板形参包,有点不好理解,还是先看一下语法看看:

template < 形参列表 > class ..
  • 12
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cpp加油站

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值