【可变参模板】可变参类模板
可变参类模板也和可变参函数模板一样,允许模板定义含有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可以通过包管理…...
label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...
Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
JavaScript 数据类型详解
JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型(Primitive) 和 对象类型(Object) 两大类,共 8 种(ES11): 一、原始类型(7种) 1. undefined 定…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...
