手机上什么网站,重庆市工程建设信息网新网站,什么优化,app推广营销可变参类模板也和可变参函数模板一样#xff0c;允许模板定义含有0到多个#xff08;任意个#xff09;模板参数。可变参类模板参数包的展开方式有多种#xff0c;以下介绍几种常见的方法。
一、递归继承展开
1.1类型模板参数包的展开
首先先看下面的代码#xff1a; /…可变参类模板也和可变参函数模板一样允许模板定义含有0到多个任意个模板参数。可变参类模板参数包的展开方式有多种以下介绍几种常见的方法。
一、递归继承展开
1.1类型模板参数包的展开
首先先看下面的代码 //类型模板展开
//泛化版本
templatetypename...Args
class myclasst {public:myclasst() {std::cout myclasst::myclasst()泛化版本执行了this this sizeof...(Args) (sizeof...(Args)) \n;}};templatetypename First, typename... Others
class myclasstFirst, Others... :private myclasstOthers... { //继承偏特化版本public:First m_i;myclasst() :m_i(0) {std::cout myclasst::myclasst()偏特化版本执行了this this sizeof...(Others) (sizeof...(Others)) \n;}};void Test1() {myclasstint, float, floatmyc;
}这里我们定义了一个可变参类模板的泛化版本通过 s i z e o f . . . sizeof... sizeof...计算出参数数量我们还打印了 t h i s this this指针。
然后我们又定义了一个偏特化类型这个偏特化类型继承了另一个偏特化版本。可以发现继承的参数列表参数减少了一个。
我们运行后得到以下结果。 容易发现 t h i s this this指针是一致的而且打印的参数是从少到多的。 因此我们可以得到结论
对于这种继承来展开的参数包我们是递归到空参调用泛化版本构造了这样的一个类作为祖宗类然后再通过这个祖宗不断通过继承链展开。
由于是继承关系所以 t h i s this this指针显然是相同的。
相应的关系如下图所示
当然如果我们对泛化版本进行一个空参的偏特化如下
//使用特殊的特化版本
template
class myclasst {
public:myclasst() {std::cout myclasst::myclasst()特殊的特化版本执行了this this \n;}
};可以发现这次最开始实例化的模板是这个空参的特化版本注意这里并不是全特化可变参模板不存在全特化 有时如果我们不需要使用泛化版本就像上面的样例直接声明即可
//直接声明
templatetypename...Args
class myclasst;1.2非类型模板参数包的展开
参数包不仅可以是类型的也可以是非类型的。 看下面的范例
//非类型模板参数的展开//泛化版本
templateauto... FArgs
class myclasst2 {
public:myclasst2() {std::cout myclasst2::myclasst2()偏特化版本执行了this this sizeof...(Others) (sizeof...(FArgs)) \n;}
};//偏特化版本
templateauto First, auto... Others
class myclasst2First, Others... :public myclasst2Others... {
public:myclasst2() {std::cout myclasst2::myclasst2()偏特化版本执行了this this sizeof...(Others) (sizeof...(Others)) ,First First \n;}
};void Test2() {myclasst21, 2LL, a, 4, 5myc;}可以发现这里的参数列表使用了 a u t o auto auto这样更灵活一些。实际上如果想要固定类型如 ( i n t , c h a r , d o u b l e ) (int,char,double) (int,char,double)等类型也可以直接使用。
对于非类型参数包的展开大体上和类型参数包展开一致这里直接调用得到
同样对于参数的展开是递归的从最后开始实例化
1.3 模板模板参数的展开
对于参数列表是模板模板参数并且是一个可变参数列表的类型我们同样可以展开写法上类似只是繁琐了些。
参考下方代码
//模板模板参数的展开
templatetypename T, templatetypenametypename... Container //泛化版本
class myclasst3 {
public:myclasst3() {std::cout myclasst3::myclasst3()泛化版本执行了this this \n;}
};//偏特化版本
templatetypename T, templatetypenametypename FirstContainer,templatetypenametypename... OtherContainers
class myclasst3T, FirstContainer, OtherContainers...:public myclasst3T, OtherContainers... {public:myclasst3() {std::cout myclasst3::myclasst3()偏特化版本执行了this this sizeof...(Others) (sizeof...(OtherContainers)) \n;}
};同样是继承链展开我们下面写一个辅助函数来查看调用的信息
//查看类型的辅助类
templatetypename T, templatetypenametypename... Container
class myclasst4 :public myclasst3T, Container... {public:myclasst4() {std::cout myclasst4::myclasst4()执行了this this , T的类型是 typeid(T).name() Container的参数个数是 (sizeof...(Container)) \n;}};
void Test3() {myclasst4int, std::vector, std::list, std::deque, std::stackmyc;}这个辅助类继承自泛化版本因此调用这个辅助类模板的时候就会展开模板模板类型参数包 这里使用了 t y p e i d typeid typeid来打印类型名可以发现实例化出的容器类都是 i n t int int类型。
二、通过递归组合方式来展开参数包
2.1 递归组合展开
前面的展开方式基于继承而递归组合方式是基于构造来展开的。 先看看下面的代码
//通过递归组合展开//泛化版本
templatetypename... args
class myclassT {
public:myclassT() {std::cout myclassT::myclassT()泛化版本执行了this this \n;}
};//偏特化版本
templatetypename First, typename...Others
class myclassTFirst, Others... {
public:First m_i;myclassTOthers...m_o;myclassT() :m_i(0) {std::cout myclassT::myclassT()偏特化版本执行了this this sizeof...(Others) sizeof...(Others) \n;}//递归构造组合myclassT(First parf, Others... paro) :m_i(parf), m_o(paro...) {std::cout ------------begin------------\n;std::cout myclassT::myclassT(parf,...paro)执行了this this \n;std::cout m_i m_i \n;std::cout ------------end------------\n\n;}};
我们省去了繁琐的继承链通过在泛化版本内部设置成员变量以及构造函数的方式来展开参数包。
我们会进行递归的构造直到参数列表为空调用泛化版本结束递归。 同样的也能用特殊的特化版本来结束递归
//特殊的特化版本
template
class myclassT {
public:myclassT() {std::cout myclassT::myclassT()特殊的特化版本执行了this this \n;}
};运行测试代码
void Test4() {myclassTint, float, double, charmyc(1, 2.0f, 3.0, 4);
}
得到以下结果
同样是递归展开所以参数的构造也是从低向上从右到左。而这一次由于不是继承关系所以 t h i s this this指针也不同。
非类型参数包和模板模板类型参数包的展开类似这里就不赘述了。
三、通过元组和递归调用展开参数包
3.1元组的基本使用
元组 t u p l e tuple tuple是 C 11 C11 C11引入的类型在元组内可以定义不同的类型下面是元组的基本使用方法
//元组的基本使用方法
void Test5() {//定义不同类型的元素std::tupleint, char, floatmytuple(0, 1, 2.0f);//取出元组第i个元素使用std::get函数模板std::cout std::get0(mytuple) \n;//后一个类型可以自动推导std::cout std::get1(mytuple) \n;std::cout std::get2(mytuple) \n;//使用std::getT返回元组类型为T的值如果存在多个相同类型会报错std::cout int类型为 std::getchar(mytuple) \n;}其中 s t d : : g e t std::get std::get是一个函数模板可以去除元组第 k k k个类型的数也可以指定类型取出相应类型的值。
注意的是如果是取出指定类型的值必须是唯一的不然将无法编译通过。
简单测试运行后得到
3.2使用元组来递归展开
借助元组可以定义不同类型的这个特点我们可以进行参数包的展开
//使用元组来递归展开
templateint mycount, int mymaxcount, typename...T
class myclassT2 {
public:static void mysfunc(const std::tupleT... t) {std::cout 获取第 mycount 1 个元素val std::getmycount(t) \n;myclassT2mycount 1, mymaxcount, T...::mysfunc(t); //自递归每次取出后一个数}
};//偏特化版本用于结束递归
templateint mymaxcount, typename...T
class myclassT2mymaxcount, mymaxcount, T... {
public:static void mysfunc(const std::tupleT... t) {std::cout 调用特化版本结束递归\n;}
};//辅助调用模板
templatetypename...T
void myfunctuple(const std::tupleT... t) {myclassT20, sizeof...(T), T...::mysfunc(t); //从0开始展开}这里 m a x c o u n t maxcount maxcount是需要展开的参数包的总大小通过 s i z e o f . . . sizeof... sizeof...求得mycount$是目前展开了多少个参数初始为0 0 0 0需要递归 1 1 1。
因此当 m a x c o u n t m y c o u n t maxcount mycount maxcountmycount时需要调用一个特化版本来结束递归。
我们写了一个辅助模板用于展开
成员函数使用静态的这样在编译期间就能确定不使用静态的也行只是这样需要创建类对象。
调用测试函数
void Test6() {std::tuplefloat, int, intmytuple(1.0f, 2, 3);myfunctuple(mytuple);
}成功展开了参数包