【可变参模板】可变参类模板
可变参类模板也和可变参函数模板一样,允许模板定义含有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);
}
成功展开了参数包:
相关文章:

【可变参模板】可变参类模板
可变参类模板也和可变参函数模板一样,允许模板定义含有0到多个(任意个)模板参数。可变参类模板参数包的展开方式有多种,以下介绍几种常见的方法。 一、递归继承展开 1.1类型模板参数包的展开 首先先看下面的代码: /…...
Linux 递归删除大量的文件
一般情况下 在 Ubuntu 中,递归删除大量文件和文件夹可以通过以下几种方式快速完成。常用的方法是使用 rm 命令,配合一些适当的选项来提高删除速度和效率。 1. 使用 rm 命令递归删除 最常见的方式是使用 rm 命令的递归选项 -r 来删除目录及其所有内容。…...

设计一个算法,找出由str1和str2所指向两个链表共同后缀的起始位置
假定采用带头结点的单链表保存单词,当两个单词有相同的后缀时,则可共享相同的后缀存储空间,例如,’loading’和’being’的存储映像如下图所示。 设str1和str2分别指向两个单词所在单链表的头结点,链表结点结构为 data…...
Python中如何判断一个变量是否为None
在Python中,判断一个变量是否为None是一个常见的需求,特别是在处理可选值、默认值或者是在函数返回结果可能不存在时。虽然这个操作本身相对简单,但围绕它的讨论可以扩展到Python的哲学、类型系统、以及如何在不同场景下优雅地处理None值。 …...

表观遗传系列1:DNA 甲基化以及组蛋白修饰
1. 表观遗传 表观遗传信息很多为化学修饰,包括 DNA 甲基化以及组蛋白修饰,即DNA或蛋白可以通过化学修饰添加附加信息。 DNA位于染色质(可视为微环境)中,并不是裸露的,因此DNA分子研究需要跟所处环境结合起…...
Android 跳转至各大应用商店应用详情页
测试通过机型品牌: 华为、小米、红米、OPPO、一加、Realme、VIVO、IQOO、荣耀、魅族、三星 import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import …...
Pywinauto鼠标操作指南
Pywinauto是一个强大的Python库,用于自动化Windows桌面应用程序的测试。它提供了一系列工具和API来模拟用户输入,包括键盘、鼠标事件,以及与各种窗口控件交互的能力。本文将详细介绍如何使用Pywinauto来执行鼠标操作,并通过一些示…...

VRAY云渲染动画怎么都是图片?
动画实际上是由一系列连续的静态图像(帧)组成的,当这些帧快速连续播放时,就形成了动画效果。每一帧都是一个单独的图片,需要单独渲染。 云渲染农场的工作方式: 1、用户将3D场景文件和动画设置上传到云渲染…...
共享内存(C语言)
目录 一、引言 二、共享内存概述 1.什么是共享内存 2.共享内存的优势 三、共享内存的实现 1.创建共享内存 2.关联共享内存 3.访问共享内存 4.解除共享内存关联 5.删除共享内存 四、共享内存应用实例 五、总结 本文将深入探讨C语言中的共享内存技术,介绍其原理、…...

《JavaEE进阶》----16.<Mybatis简介、操作步骤、相关配置>
本篇博客讲记录: 1.回顾MySQL的JDBC操作 2..Mybatis简介、Mybatis操作数据库的步骤 3.Mybatis 相关日志的配置(日志的配置、驼峰自动转换的配置) 前言 之前学习应用分层时我们知道Web应用程序一般分为三层,Controller、Service、D…...

HuggingFists算子能力扩展-PythonScript
HuggingFists作为一个低代码平台,很多朋友会关心如何扩展平台算子能力。扩展平台尚不支持的算子功能。本文就介绍一种通过脚本算子扩展算子能力的解决方案。 HuggingFists支持Python和Javascript两种脚语言的算子。两种语言的使用方式相同,使用者可以任选…...
WInform记录的添加和显示
1、程序 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms;namespace ComboBoxApp {public part…...

★ C++基础篇 ★ string类的实现
Ciallo~(∠・ω< )⌒☆ ~ 今天,我将继续和大家一起学习C基础篇第五章下篇----string类的模拟实现 ~ 上篇:★ C基础篇 ★ string类-CSDN博客 C基础篇专栏:★ C基础篇 ★_椎名澄嵐的博客-CSDN博客 目录 一 基础结构 二 迭代器 …...
rman compress
级别 初始 备完 耗时 low 1804 3572 0:10 High 1812 3176 2:00 MEDIUM 1820 3288 0:13 BASIC 1828 3444 0:56 ---不如MEDIUM,时间还长 NO COMPRESS 1820 5924 0:5 R…...
创建一个Oracle版本的JDK的Docker镜像
背景说明 OpenJDK 和Oracle JDK 一般情况下我们选择OpenJDK,两者针对大部分场景都可以满足,有些地方例如反射技术获得某些包路径下的类对象等,有时候选择OpenJDK会导致空指针异常。 两者在底层实现方面有部分区别。 创建镜像 这里是Linux…...

Harmony OS DevEco Studio 如何导入第三方库(以lottie为例)?-- HarmonyOS自学2
在做鸿蒙开发时,离不开第三方库的引入 一.有哪些支持的Harmony OS的 第三方库? 第三方库下载地址: 1 tpc_resource: 三方组件资源汇总 2 OpenHarmony三方库中心仓 二. 如何加入到DevEco Studio工程 以 lottie为例 OpenHarmony-TPC/lot…...

JAVA数据导出为Excel
目录 一、导入依赖 二、使用的相关类 1、XSSFWorkbook 构造方法 创建表 操作表 保存表 样式和格式 日期处理 密码保护 其他 2、XSSFSheet 获取属性和信息 行操作 列操作 表的属性 合并单元格 保护表 页眉和页脚 注释 其它 3、XSSFRow 获取属性和信息 单…...

【数据结构与算法 | 灵神题单 | 快慢指针(链表)篇】力扣876, 2095, 234
1. 力扣876:链表的中间节点 1.1 题目: 给你单链表的头结点 head ,请你找出并返回链表的中间结点。 如果有两个中间结点,则返回第二个中间结点。 示例 1: 输入:head [1,2,3,4,5] 输出:[3,4,…...

第十五届蓝桥杯图形化省赛题目及解析
第十五届蓝桥杯图形化省赛题目及解析 一. 单选题 1. 运行以下程序,角色会说( )? A、29 B、31 C、33 D、35 正确答案:C 答案解析: 重复执行直到m>n不成立,即重复执行直到m<n。所有当m小于或者 等于n时&…...
linux下NTP服务器实战(chrony软件)
linux下NTP服务器实战(chrony软件) 记录linux下NTP服务器搭建及相关管理操作,使用chrony软件包安装部署。相比ntp服务,Chrony服务适用于更高精度、更高稳定性、自动化等场景。 1. 安装 chrony 在大多数Linux发行版上,chrony可以通过包管理…...

(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
LCTF液晶可调谐滤波器在多光谱相机捕捉无人机目标检测中的作用
中达瑞和自2005年成立以来,一直在光谱成像领域深度钻研和发展,始终致力于研发高性能、高可靠性的光谱成像相机,为科研院校提供更优的产品和服务。在《低空背景下无人机目标的光谱特征研究及目标检测应用》这篇论文中提到中达瑞和 LCTF 作为多…...

篇章二 论坛系统——系统设计
目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…...
OCR MLLM Evaluation
为什么需要评测体系?——背景与矛盾 能干的事: 看清楚发票、身份证上的字(准确率>90%),速度飞快(眨眼间完成)。干不了的事: 碰到复杂表格(合并单元…...

李沐--动手学深度学习--GRU
1.GRU从零开始实现 #9.1.2GRU从零开始实现 import torch from torch import nn from d2l import torch as d2l#首先读取 8.5节中使用的时间机器数据集 batch_size,num_steps 32,35 train_iter,vocab d2l.load_data_time_machine(batch_size,num_steps) #初始化模型参数 def …...