副标题#e#
最近的C++语言尺度有些更进一步的巨大特性,诸如加上了变长模板。我在实验领略这个特性的进程中
的一个最大的问题是,没有足够简朴的代码示例来说明到底变长模板是如何利用和起浸染的。
以下是 我的一个根基样例来说明变长模板:
template <class …A> int func(A… arg)
{
return sizeof…(arg);
}
int main(void)
{
return func (1,2,3,4,5,6);
}
首要先容的是一些术语: 一个模板参数此刻可以是一个模板参数包, <class…A>。一个模板参数包可以代表任意数量的
模板参数。在以上这个样例中,模板 <class…A>界说了func这个函数,他拥有任意数量的函数参数。函数参数(A… arg)即为
一个函数 参数包,他以一个参数的"形式"代表了模板template <class…A>参数包的每个成员。在这 个示例中,我们利用了6个参数来挪用函数func。
模板参数推导(template argument deduction)会将参数 包<class…A>推导成为
<int,int,int,int,int,int>,接着函数参数包酿成 (int,int,int,int,int,int),正好对应了6个通报过来的整形参数。
变长操纵符sizeof…简朴的返回了参 数包的参数个数(函数可能模板的),功效为6.
虽然任何参数包多可觉得空,思量以下代码示 例:
template <class …A> int func(A… arg)
{
return sizeof… (arg);
}
int main(void)
{
return func();
}
#p#副标题#e#
在这个示例中模板参数包被推 导成为空,使得函数参数包即为空,那么我们便不消通报任何参数也能举办函数挪用,这里,sizeof…的返 回功效为0。
别的,参数包中的参数也并不必然要同样的范例:
template <class …A> int func(A… arg)
{
return sizeof…(arg);
}
struct s1{}; struct s2{}; struct s3{};
int main(void)
{
s1 t1; s2 t2; s3 t3;
return func(t1,t2,t3);
}
在这个示例中参数包被推导为<s1,s2,s3>。
在之前的例子中,参数包都是范例(type parameter)参 数, 虽然我们也可以指定非范例参数(non-type parameter), 好比:
template <bool …A> int func()
{
return sizeof…(A);
}
int main(void)
{
return func<true,false,true,false>();
}
以上这个例子展示了变长模板的一些根基语法。获得参数包 中元素个数很简朴,可是假如一旦有元素不行被会见,那么参数包的浸染
将会很是有限。
为了访 问函数模板中的参数包,我们可以重载函数, 见以下示例:
#include <iostream>
using namespace std;
void func()
{
cerr << "EMPTY" << endl;
}
template <class A, class …B> void func(A argHead, B… argTail)
{
cerr << "A: " << argHead << endl;
func (argTail…);
}
int main(void)
{
func(1,2,3,4,5,6);
return 0;
}
在这个示例中,我们有两个函数名匹配函数挪用,其一长短模板。由于非模板的谁人匹配没有相应 的函数参数,因此我们实际上只有一个匹配,
所有的相应挪用应该选用模板的谁人匹配。为了匹配一个模 板函数,编译器不得不从函数挪用的实参推导模板参数,而且实例化模板函数。这个
模板函数的第一个参 数是普通的模板参数<class A>,第二个是函数参数包。第一个模板参数会被推导成第一个参数的范例 。参数包中的每个成员范例
会基于所有还未推导的元素做推导(deduction)。
我们一开始便在main函数 中挪用func(1,2,3,4,5,6),模板参数<class A>被推导成int,模板参数包<class…B>被推导成 <int,int,int,int,int>。
编译器实例化了函数模板void func(int,int,int,int,int,int)。在函数 体内部的实例化,argTail…是包扩展(pack expansion)。一个包扩展是由
编译器按照上下文语境来举办 扩展的。在这个示例中,argTail就是(2,3,4,5,6). 第一个参数是argHead,他此刻是一个正常的函数参数。
接着,我们继承函数挪用的进程,此刻挪用是func(2,3,4,5,6),我们开始参数推导,在参数推导完成 之后,此刻模板参数<class A>被推导为<int>,
模板参数包<class…B>被推导成为 <int,int,int,int>。在函数体内部argHead此刻就是2,argTail就是(3,4,5,6),新的函数挪用是func (3,4,5,6)
同理,我们继承函数挪用的进程,轮回来去。下一次推导之后argHead是3,argTail是 (4,5,6), 新的函数挪用是func(4,5,6)。再接下来,
下一次挪用的进程,参数推导之后argHead是4, argTail是(5,6),新的函数挪用是func(5,6)。再接着,argHead是5,argTail是(6),新的函数挪用
是 func(6)。继承下一步,argHead是6,argTail是(),新的函数挪用是func()。到了这里,我们发明此刻的函数 挪用匹配了非模板的谁人func函数!
因此,基于以上阐明,输出功效为:
A: 1
A: 2
A: 3
A: 4
A: 5
A: 6
EMPTY