【C++11】可变参数模板(函数模板、类模板)
在C++11之前,类模板和函数模板只能含有固定数量的模板参数。C++11增强了模板功能,允许模板定义中包含0到任意个模板参数,这就是可变参数模板。可变参数模板的加入使得C++11的功能变得更加强大,而由此也带来了许多神奇的用法。
可变参数模板
可变参数模板和普通模板的语义是一样的,只是写法上稍有区别,声明可变参数模板时需要在typename或class后面带上省略号...:
template<typename... Types>
其中,...可接纳的模板参数个数是0个及以上,当然也包括0个。
若不希望产生模板参数个数为0的变长参数模板,则可以采用以下的定义:
template<typename Head, typename... Tail>
本质上,...可接纳的模板参数个数仍然是0个及以上的任意数量,但由于多了一个Head类型,由此该模板可以接纳1个及其以上的模板参数。
函数模板的使用
在函数模板中,可变参数模板最常见的使用场景是以递归的方法取出可用参数:
void print() {}template<typename T, typename... Types>
void print(const T& firstArg, const Types&... args)
{std::cout << firstArg << " " << sizeof...(args) << std::endl;print(args...);
}
通过设置...,可以向print函数传递任意个数的参数,并且各个参数的类型也是任意。也就是说,可以允许模板参数接受任意多个不同类型的不同参数。这就是不定参数的模板,格外需要关注的是,...三次出现的位置。
如果如下调用print函数:
print(2, "hello", 1);
如此调用会递归将3个参数全部打印。细心的话会发现定义了一个空的print函数,这是因为当使用可变参数的模板,需要定义一个处理最后情况的函数,如果不写,会编译错误。这种递归的方式,是不是觉得很惊艳!
在不定参数的模板函数中,还可以通过如下方式获得args的参数个数:
sizeof...(args)
假设,在上面代码的基础上再加上一个模板函数如下,那么运行的结果是什么呢?
#include <iostream>void print() {}template<typename T, typename... Types>
void print(const T& firstArg, const Types&... args)
{std::cout << firstArg << " " << sizeof...(args) << std::endl;print(args...);
}template <typename... Types>
void print(const Types&... args)
{std::cout << "print(...)" << std::endl;
}int main(int argc, char* argv[])
{print(2, "hello", 1);return 0;
}
现在有一个模板函数接纳一个参数加上可变参数,还有一个模板函数直接接纳可变参数,如果调用print(2, “hello”, 1),会发现这两个模板函数的参数格式都符合。是否会出现冲突、不冲突的话会执行哪一个呢?
运行代码后的结果为:
yngzmiao@yngzmiao-virtual-machine:~/test/$ ./main
2 2
hello 1
1 0
从结果上可以看出,程序最终选择了一个参数加上不定参数的模板函数。也就是说,当较泛化和较特化的模板函数同时存在的时候,最终程序会执行较特化的那一个。
再比如一个例子,std::max函数只可以返回两个数的较大者,如果多个数,就可以通过不定参数的模板来实现:
#include <iostream>template <typename T>
T my_max(T value)
{return value;
}template <typename T, typename... Types>
T my_max(T value, Types... args)
{return std::max(value, my_max(args...));
}int main(int argc, char* argv[])
{std::cout << my_max(1, 5, 8, 4, 6) << std::endl;return 0;
}
类模板的使用
除了函数模板的使用外,类模板也可以使用不定参数的模板参数,最典型的就是tuple类了。其大致代码如下:
#include <iostream>template<typename... Values> class tuple;
template<> class tuple<> {};template<typename Head, typename... Tail>
class tuple<Head, Tail...>: private tuple<Tail...>
{typedef tuple<Tail...> inherited;public:tuple() {}tuple(Head v, Tail... vtail) : m_head(v), inherited(vtail...) {}Head& head() {return m_head;}inherited& tail() {return *this;}protected:Head m_head;
};int main(int argc, char *argv[])
{tuple<int, float, std::string> t(1, 2.3, "hello");std::cout << t.head() << " " << t.tail().head() << " " << t.tail().tail().head() << std::endl;return 0;
}
根据代码可以知道,tuple类继承除首之外的其他参数的子tuple类,以此类推,最终继承空参数的tuple类。继承关系可以表述为:
tuple<>↑
tuple<std::string>string "hello"↑
tuple<float, std::string>float 2.3↑
tuple<int, float, std::string>int 1
接下来考虑在内存中的分布,内存中先存储父类的变量成员,再保存子类的变量成员,也就是说,对象t按照内存分布来说;
┌─────────┐<---- 对象指针
| hello |
|─────────|
| 2.3 |
|─────────|
| 1 |
└─────────┘
这时候就可以知道下一句代码的含义了:
inherited& tail() {return *this;}
tail()函数返回的是父类对象,父类对象和子类对象的内存起始地址其实是一样的,因此返回*this,再强行转化为inherited类型。
当然,上面采用的是递归继承的方式,除此之外,还可以采用递归复合的方式:
template<typename... Values> class tup;
template<> class tup<> {};template<typename Head, typename... Tail>
class tup<Head, Tail...>
{typedef tup<Tail...> composited;public:tup() {}tup(Head v, Tail... vtail) : m_head(v), m_tail(vtail...) {}Head& head() {return m_head;}composited& tail() {return m_tail;}protected:Head m_head;composited m_tail;
};
两种方式在使用的过程中没有什么区别,但C++11中采用的是第一种方式(递归继承)。
在上面的例子中,取出tuple中的元素是一个比较复杂的操作,需要不断地取tail,最终取head才能获得。标准库的std::tuple,对此进行简化,还提供了一些其他的函数来进行对tuple的访问。例如:
#include <iostream>
#include <tuple>int main(int argc, char *argv[]) {std::tuple<int, float, std::string> t2(1, 2.3, "hello");std::get<0>(t2) = 4; // 修改tuple内的元素std::cout << std::get<0>(t2) << " " << std::get<1>(t2) << " " << std::get<2>(t2) << std::endl; // 获取tuple内的元素auto t3 = std::make_tuple(2, 3.4, "World"); // make方法生成tuple对象std::cout << std::tuple_size<decltype(t3)>::value << std::endl; // 获取tuple对象元素的个数std::tuple_element<1, decltype(t3)>::type f = 1.2; // 获取tuple对象某元素的类型return 0;
}
相关阅读
- C++11相关知识点

相关文章:
【C++11】可变参数模板(函数模板、类模板)
在C11之前,类模板和函数模板只能含有固定数量的模板参数。C11增强了模板功能,允许模板定义中包含0到任意个模板参数,这就是可变参数模板。可变参数模板的加入使得C11的功能变得更加强大,而由此也带来了许多神奇的用法。 可变参数模…...
centos安装高版本cmake
之前centos版本为cmake version 2.8.12.2采用yum remove卸载后重装还是这个版本,看来centos下面就是这个最新了,这说明centos煞笔。于是自己下载cmake包,然后安装。 官方cmake链接地址(3.16)(其他版本自己找,链接给你了) 1,wget下载 2,解压: tar -zxf cmake-3.16.0.…...
重温一下C#的时间类型,并简单写一个定时器功能
🎉🎉 时间是一个非常抽象的概念,本篇文章我们不深究目前电脑上的时候是如何保持全网同步。主要是讲讲在使用C#编程语言里的时间类型。最后使用定时任务简单写一个提醒功能,比如:每天10点准时打开一次csdn首页ÿ…...
MYSQL查询语句执行顺序
SQL语句定义的顺序 (1) SELECT (2)DISTINCT <select_list> (3) FROM <left_table> (4) <join_type> JOIN <right_table> (5) ON <join_condition> (6) WHERE <where_condition> (7) GROUP BY <group_by_list> (8) WITH {C…...
总结:电容在电路35个基本常识
1 电压源正负端接了一个电容,与电路并联,用于整流电路时,具有很好的滤波作用,当电压交变时,由于电容的充电作用,两端的电压不能突变,就保证了电压的平稳。 当用于电池电源时,具有交流…...
Kroger EDI 855 采购订单确认报文详解
本文着重讲述Kroger EDI项目中,供应商发给Kroger的X12 855EDI 规范报文(采购订单确认)解读。 在此前的文章如何读懂X12报文中,我们对X12已经做了详细的介绍,大家可以以此为基础,深入了解855采购订单确认报…...
HANA SDA-远程数据源访问
我们需要把其他系统的数据拿过来,到BW里和财务的数据集成。 HANA SDA就是不复制数据,建立虚拟表(virtual table)来映射到远程数据源。通过这个虚拟表访问其他系统的数据。 对虚拟表的操作现在也可以查询,更新ÿ…...
【AUTOSAR】:OS-Hook
OS-Hook OS-HookPINIC类型1、Os_ErrKernelPanic1.1、Os_HookCallPanicHook1.1.1、OS_PANICHOOK1.1.1.1、Os_PanicHook1.1.1.2、Os_Hal_CoreFreezeOs_Hal_NOPOS-Hook 延伸阅读 延伸阅读 PINIC类型 1、Os_ErrKernelPanic...
Open3d入门
目录 点云数据 1 主成分分析 1.1 Method 1.2 Results 2 表面法线估计 2.1 Method 2.2 Results 3 体素网格下采样 3.1 Method 3.2 Results 点云数据 常用数据下载(免积分) 1 主成分分析 1.1 Method 对点云进行主成分分析(PCA&…...
linux部署zookeeper
linux部署zookeeper 1、单机部署zk ZooKeeper服务器是用Java创建的,它需要在JVM上运行,所以需要使用JDK1.6及以上版本,一般都是jdk1.8。 选择自己安装本地的jdk,而不是centos自带的openjdk。 查看本地安装的jdk: j…...
Junit4升级Junit5汇总
Junit4升级Junit5汇总目录MockMvcBuildersUnnecessaryStubbingException目录 记录Junit4升级到Junit5中遇到的问题和结局方案 MockMvcBuilders 问题: 将Junit4的RunWith和Rule都改成ExtendWith后出现setup函数中MockMvcBuilders的参数不正确 ExtendWith({Spring…...
Axios二次封装和Api的解耦
目录 一、axios三种基本写法 二、axios的二次封装 三、Api的解耦 一、axios三种基本写法 1)get方法(是最简单的): 写法二: 2)post: 3)axios请求配置 默认是get请求,如…...
SpringAOP从入门到源码分析大全,学好AOP这一篇就够了(一)
文章目录系列文档索引一、认识AOP1、AOP的引入原因2、AOP常见使用场景日志场景统计场景安防场景性能场景3、AOP概念AOP 的概念Aspect 概念(切面)Join point 概念(连接点)Pointcut 概念(切入点)Advice 概念&…...
【单目标优化算法】樽海鞘群算法(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
手把手教你,解决C盘分区不足,C盘怎么扩大磁盘分区
由于在磁盘分区中,C盘是很重要的一个磁盘,为了保证C盘有足够的磁盘分区。其中扩大C盘分区很常见的操作之一。那么,C盘怎么扩大磁盘分区?在本文中,易我小编将全面地讲解C盘合并分区的方法。 一、为什么C盘怎么扩大磁盘分…...
Ethernet-APL——过程自动化的新黄金标准
| Ethernet-APL为终客户和设备制造商带来益处 Ethernet-APL(Advanced Physical Layer,高级物理层)是一种两线制以太网物理层,它使用了由IEEE 802.3cg所定义的10BASE-T1L,并采用了新的工艺制造规定,因此构成…...
LVGL Styles
LVGL StylesGet started按钮添加标签按钮添加风格滑动条值显示StylesSize stylesBackground stylesBorder stylesOutline stylesShadow stylesImage stylesArc stylesText stylesLine stylesGet started 按钮添加标签 /*** brief 按钮事件回调函数* param e */ void btn_eve…...
扬帆优配|联通港股创近两年新高!A股资源类股爆发,食品饮料领跌
今日上午,A股商场和港股商场均现较大起伏震动,临近上午收盘出现一波跳水,不过,到上午收盘,上证指数仍微涨0.10%,煤炭等资源类板块明显上涨。 港股商场上午走弱,科技股领跌。 沪指微涨0.10%资源…...
Win10+VS2019+Qt5.15.2下编译QCAD
一:官方说法:WindowsDownload and install a C compiler, for example:Visual Studio C Express or Visual Studio CommunityDownload and install Qt from qt.io (see supported platforms):Download for example the online installer fileqt-opensour…...
【微信小程序】原生微信小程序ts模板下引入vant weapp
之前一直是在普通项目下使用 vant weapp,这不最近学了ts,使用微信开发工具的tsless初始化项目,再引入 vant 时踩了好久坑,特来记录一下 前言 本文章适合微信开发工具的ts项目,指的是项目目录结构如下图 总结 从上图…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...
【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...
【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...
