当前位置: 首页 > news >正文

【可变参模板】可变参类模板

可变参类模板也和可变参函数模板一样,允许模板定义含有0到多个(任意个)模板参数。可变参类模板参数包的展开方式有多种,以下介绍几种常见的方法。

一、递归继承展开

1.1类型模板参数包的展开

首先先看下面的代码:


//类型模板展开
//泛化版本
template<typename...Args>
class myclasst {public:myclasst() {std::cout << "myclasst::myclasst()泛化版本执行了,this = " << this<< " sizeof...(Args) = " << (sizeof...(Args)) << "\n";}};template<typename First, typename... Others>
class myclasst<First, Others...> :private myclasst<Others...> { //继承偏特化版本public:First m_i;myclasst() :m_i(0) {std::cout << "myclasst::myclasst()偏特化版本执行了,this = " << this<< " sizeof...(Others) = " << (sizeof...(Others)) << "\n";}};void Test1() {myclasst<int, float, float>myc;
}

这里,我们定义了一个可变参类模板的泛化版本,通过 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";}
};

可以发现,这次最开始实例化的模板是这个空参的特化版本,注意这里并不是全特化(可变参模板不存在全特化)
在这里插入图片描述
有时,如果我们不需要使用泛化版本(就像上面的样例),直接声明即可:

//直接声明
template<typename...Args>
class myclasst;

1.2非类型模板参数包的展开

参数包不仅可以是类型的,也可以是非类型的。
看下面的范例:

//非类型模板参数的展开//泛化版本
template<auto... FArgs>
class myclasst2 {
public:myclasst2() {std::cout << "myclasst2::myclasst2()偏特化版本执行了,this = " << this<< " sizeof...(Others) = " << (sizeof...(FArgs)) << "\n";}
};//偏特化版本
template<auto First, auto... Others>
class myclasst2<First, Others...> :public myclasst2<Others...> {
public:myclasst2() {std::cout << "myclasst2::myclasst2()偏特化版本执行了,this = " << this<< " sizeof...(Others) = " << (sizeof...(Others)) << ",First = " << First << "\n";}
};void Test2() {myclasst2<1, 2LL, 'a', 4, 5>myc;}

可以发现,这里的参数列表使用了 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 模板模板参数的展开

对于参数列表是模板模板参数,并且是一个可变参数列表的类型,我们同样可以展开,写法上类似,只是繁琐了些。

参考下方代码:

//模板模板参数的展开
template<typename T, template<typename>typename... Container> //泛化版本
class myclasst3 {
public:myclasst3() {std::cout << "myclasst3::myclasst3()泛化版本执行了,this = " << this << "\n";}
};//偏特化版本
template<typename T, template<typename>typename FirstContainer,template<typename>typename... OtherContainers>
class myclasst3<T, FirstContainer, OtherContainers...>:public myclasst3<T, OtherContainers...> {public:myclasst3() {std::cout << "myclasst3::myclasst3()偏特化版本执行了,this = " << this<< " sizeof...(Others) = " << (sizeof...(OtherContainers)) << "\n";}
};

同样是继承链展开,我们下面写一个辅助函数来查看调用的信息:

//查看类型的辅助类
template<typename T, template<typename>typename... Container>
class myclasst4 :public myclasst3<T, Container...> {public:myclasst4() {std::cout << "myclasst4::myclasst4()执行了,this = " << this << ", T的类型是:"<< typeid(T).name() << ",Container的参数个数是" << (sizeof...(Container)) << "\n";}};
void Test3() {myclasst4<int, std::vector, std::list, std::deque, std::stack>myc;}

这个辅助类继承自泛化版本,因此调用这个辅助类模板的时候,就会展开模板模板类型参数包:

在这里插入图片描述
这里使用了 t y p e i d typeid typeid来打印类型名,可以发现实例化出的容器类都是 i n t int int类型。

二、通过递归组合方式来展开参数包

2.1 递归组合展开

前面的展开方式基于继承,而递归组合方式是基于构造来展开的。
先看看下面的代码:

//通过递归组合展开//泛化版本
template<typename... args>
class myclassT {
public:myclassT() {std::cout << "myclassT::myclassT()泛化版本执行了,this = " << this << "\n";}
};//偏特化版本
template<typename First, typename...Others>
class myclassT<First, Others...> {
public:First m_i;myclassT<Others...>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() {myclassT<int, float, double, char>myc(1, 2.0f, 3.0, '4');
}

得到以下结果:
在这里插入图片描述

同样是递归展开,所以参数的构造也是从低向上,从右到左。而这一次,由于不是继承关系,所以 t h i s this this指针也不同。
在这里插入图片描述

非类型参数包和模板模板类型参数包的展开类似,这里就不赘述了。

三、通过元组和递归调用展开参数包

3.1元组的基本使用

元组 t u p l e tuple tuple C + + 11 C++11 C++11引入的类型,在元组内可以定义不同的类型,下面是元组的基本使用方法:

//元组的基本使用方法
void Test5() {//定义不同类型的元素std::tuple<int, char, float>mytuple(0, '1', 2.0f);//取出元组第i个元素,使用std::get<>函数模板std::cout << std::get<0>(mytuple) << "\n";//后一个类型可以自动推导std::cout << std::get<1>(mytuple) << "\n";std::cout << std::get<2>(mytuple) << "\n";//使用std::get<T>返回元组类型为T的值,如果存在多个相同类型会报错std::cout << "int类型为:" << std::get<char>(mytuple) << "\n";}

其中 s t d : : g e t < > std::get<> std::get<>是一个函数模板,可以去除元组第 k k k个类型的数,也可以指定类型取出相应类型的值。

注意的是,如果是取出指定类型的值,必须是唯一的,不然将无法编译通过。

简单测试,运行后得到:
在这里插入图片描述

3.2使用元组来递归展开

借助元组可以定义不同类型的这个特点,我们可以进行参数包的展开:

//使用元组来递归展开
template<int mycount, int mymaxcount, typename...T>
class myclassT2 {
public:static void mysfunc(const std::tuple<T...>& t) {std::cout << "获取第" << mycount + 1 << "个元素,val = " << std::get<mycount>(t) << "\n";myclassT2<mycount + 1, mymaxcount, T...>::mysfunc(t); //自递归,每次取出后一个数}
};//偏特化版本,用于结束递归
template<int mymaxcount, typename...T>
class myclassT2<mymaxcount, mymaxcount, T...> {
public:static void mysfunc(const std::tuple<T...>& t) {std::cout << "调用特化版本结束递归!\n";}
};//辅助调用模板
template<typename...T>
void myfunctuple(const std::tuple<T...>& t) {myclassT2<0, 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 maxcount=mycount时,需要调用一个特化版本来结束递归。

我们写了一个辅助模板,用于展开:
在这里插入图片描述

成员函数使用静态的,这样在编译期间就能确定,不使用静态的也行,只是这样需要创建类对象。

调用测试函数:

void Test6() {std::tuple<float, int, int>mytuple(1.0f, 2, 3);myfunctuple(mytuple);
}

成功展开了参数包:

在这里插入图片描述

http://www.lryc.cn/news/436381.html

相关文章:

  • Linux 递归删除大量的文件
  • 设计一个算法,找出由str1和str2所指向两个链表共同后缀的起始位置
  • Python中如何判断一个变量是否为None
  • 表观遗传系列1:DNA 甲基化以及组蛋白修饰
  • Android 跳转至各大应用商店应用详情页
  • Pywinauto鼠标操作指南
  • VRAY云渲染动画怎么都是图片?
  • 共享内存(C语言)
  • 《JavaEE进阶》----16.<Mybatis简介、操作步骤、相关配置>
  • HuggingFists算子能力扩展-PythonScript
  • WInform记录的添加和显示
  • ★ C++基础篇 ★ string类的实现
  • rman compress
  • 创建一个Oracle版本的JDK的Docker镜像
  • Harmony OS DevEco Studio 如何导入第三方库(以lottie为例)?-- HarmonyOS自学2
  • JAVA数据导出为Excel
  • 【数据结构与算法 | 灵神题单 | 快慢指针(链表)篇】力扣876, 2095, 234
  • 第十五届蓝桥杯图形化省赛题目及解析
  • linux下NTP服务器实战(chrony软件)
  • Java设计模式之命令模式介绍和案例示范
  • Leetcode面试经典150题-74.搜索二维矩阵
  • 【数字集成电路与系统设计】基本的组合逻辑电路
  • 11. 建立你的第一个Web3项目
  • 衡石分析平台使用手册-容器部署
  • 静态库,动态库以及makefile基础
  • Python基础语法(1)上
  • 使用 Python/java/go做一个微信机器人
  • 【北京迅为】iTOP-i.MX6开发板使用手册第四部分固件编译第十四章非设备树Android4.4系统编译
  • 测评造假?Mistral首个多模态模型Pixtral 12B发布
  • 【Java-简单练习题】