专业编程基础技术教程

网站首页 > 基础教程 正文

浅谈C++11(第9篇 可变参数模板) c++可变参数模板类

ccvgpt 2024-10-10 05:04:00 基础教程 44 ℃

1、概述

我们知道模板通过对类型的泛化,让类或者函数具有更广泛的适应性。c++11进一步拓展了模板的范型能力,类型参数的个数可以是不确定的,这被称为可变参数模板(Variadic templates)。

可变参数模板可以采用任意类型和任意数量的模板参数,下面是一个可变参数的类模板定义:

浅谈C++11(第9篇 可变参数模板) c++可变参数模板类

“...”表示Arguments的个数和类型都是不确定的,容易知道,象下面这样创建此类模板的实例都是合法的:

当然,模板参数个数是0也是合法的,比如:

用户也可以把确定参数和可变参数混合起来,象下面这样声明模板:

这时候,用户实例化时必须至少指定一个模板参数,上面的例子中,v4的声明就变成非法的了(因为类型T没有确定)。不过话又说回来,如果模板声明时T有默认类型,比如:

则v4的声明又是有效的了(T被确定为int类型)。

函数模板的创建与上述方式类似,比如:

特别的,为了支持引用传参,可以这样声明:


2、可变参数模板的展开

为了让可变参数模板工作,得有一定的方法对每个参数执行一定的操作,这个过程可以叫展开,一般常用的方法是递归方式展开,通过对模板的特化,使模板实例化过程形成一系列模板的实例化,从而巧妙的展开模板。

假设我们要定义一个函数模板,需要打印任意个输入参数,中间用空格隔开,最后输出一个换行,可以如下定义:

递归调用过程一目了然(std::forward其实没什么作用,因为我们输出时并没有(也没必要)移动变量),假如我们用templatePrint(1,2,"123",10.5+3)来调用这个模板,则编译器会按照最佳匹配的原则依次实例化出如下版本的函数:

这些函数依次执行会按预期打印出所有的参数,这个方案的缺点是所谓的代码膨胀,会增加编译时间和可执行文件的体积。

可变参数的类模板的展开可以采用递归继承的方式进行,参数个数多的类层层继承参数个数少一个的类,思想和上述方案类似,这里不再赘述;


3、另一种展开方式

c++11提供的初始化列表支持'...'运算符,所以我们可以通过初始化列表来展开一个函数模板,还以上面的例子为例,可以做如下实现:

这个方案简化了很多,没有递归过程,利用初始化列表和逗号表达式依次对每个参数调用输出的操作,逗号表达式是为了使每一项最终有个输出值0。代码中的v是没有实际用处的,void的那一行是为了消除某些编译器对未使用变量的告警。

4、应用的例子

使用可变参数模板常常可以达到一些神奇的效果,模板编程的乐趣也在于此,这里仅举个例子,来源于小编开发的一个HTTP应用框架(https://github.com/an-tao/drogon),我们知道,HTTP应用常常需要对特定的路径做特定的处理,所以我希望框架能提供尽量灵活方便的处理函数注册接口,使用可变参数模板技术,最后达成的效果是这样的:

可以看到,本例子中,我们把一个lambda表达式注册到形如“/api/v1/handle1/{1}/{2}/?p3={3}&p4={4}”的路径上,框架把路径上占位符号代表的参数转换成对应类型的变量,按箭头所示的映射方式传入这个HTTP请求处理函数。

为了给HTTP应用开发提供最大的灵活性,我们不应该对这个处理函数的参数类型和参数个数做任何限定,这个场景里,除了用可变参数模板(当然还要配合类型萃取等模板技术),用其他技术是很难达到这样的效果的。

具体的实现,列在这里就显得过于累赘了,感兴趣的朋友可以自行去github上浏览,欢迎加星,更欢迎提出宝贵意见,这里也算是小编夹带私货了,不过也算切题,吼吼。


上一篇内容:第8篇 引用折叠和完美转发

最近发表
标签列表