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

《C++ Primer Plus》第18章:探讨 C++ 新标准(6)

可变参数模板

可变参数模板(variadic template)让您能够创建这样的模板函数和模板类,即可接收可变数量的参数。这里介绍可变参数模板函数。例如,假设要编写一个函数,它可接受任意数量的参数,参数的类型只需是 cout 能够显示的即可,并将参数显示为用逗号分隔的列表。请看下面的代码:

int n = 14;
double x = 2.71828;
std::string mr = "Mr. String objects!";
show_list(n, x);
show_list(x*x, '!', 7, mr);

这里的目标是,定义 show_list(),让上述代码能够通过编译并生成如下输出:

14, 2.71828
7.3805, !, 7, Mr. String objects!

要创建可变参数模板,需要理解几个要点:

  • 模板参数包(parameter pack);
  • 函数参数包;
  • 展开(unpack)参数包;
  • 递归。

模板和函数参数包

为理解参数包的工作原理,首先来看一个简单的模板函数,它显示一个只有一项的列表:

template<typename T>
void show_list0(T value) {std::cout << value << ", ";
}

在上述定义中,有两个参数列表。模板参数列表只包含T,而函数参数列表只包含 value。下面的函数调用将模板参数列表中的 T 设置为 double,将函数参数列表中的 value 设置为 2.15:

show_list0(2.15);

C++提供了一个用省略号表示的元运算符(meta-operator),让您能够声明表示模板参数包的标识符,模板参数包基本上是一个类型列表。同样,它还让您能够声明表示函数参数包的标识符,而函数参数包基本上是一个值列表。其语法如下:

template<typename...Args>		// Args is a template parameter pack
void show_list1(Args...args)	// args is a function parameter pack
{
...
}

其中,Args 是一个模板参数包,而 args 是一个函数参数包。与其他参数名一样,可将这些参数包的名称指定为任何符合 C++ 标识符规则的名称。Args 和 T 的差别在于,T 与一种类型匹配,而 Args 与任意数量(包括零)的类型匹配。请看下面的函数调用:

show_list1('S', 80, "sweet", 4.5);

在这种情况下,参数包 Args 包含与函数调用中的参数匹配的类型:char、int、const char * 和 double。

下面的代码指出 value 的类型为 T:

void show_list0(T value)

同样,下面的代码指出 args 的类型为 Args:

void show_list1(Args... args)		// args is a function parameter pack

更准确地说,这意味着函数参数包 args 包含的值列表与模板参数包 Args 包含的类型列表匹配——无论是类型还是数量。在上面的示例中,args 包含值’S’、80、“sweet” 和 4.5。

这样,可变参数模板 show_list1() 与下面的函数调用都匹配:

show_list1();
show_list1(99);
show_list1(88.5, "cat");
show_list1(2,4,6,8, "who do we", std::string("appreciate));

就最后一个函数调用而言,模板参数包 Args 包含类型 int、int、int、int、const char * 和 std::string,而函数参数包 args 包含值 2、4、6、8、“who do we” 和 std::string(“appreciate”)。

展开参数包

但函数如何访问这些包的内容呢?索引功能在这里不适用,即您不能使用 Args[2] 来访问包中的第三个类型。相反,可将省略号放在函数参数包名的左边,将参数包展开。例如,请看下述有缺陷的代码:

template<typename...Args> // Args is a template parameter pack
void show_list1(Args... args)	// args is a function parameter pack
{show_list1(args...);	// passes unpacked args to show_list1()
}

这是什么意思呢?为何说它存在缺陷?

假设有如下函数调用:

show_list1(5, 'L', 0.5);

这将把5、‘L’ 和 0.5 封装到 args 中。在该函数内部,下面的调用:

show_list1(5, 'L', 0.5);

将展开成如下所示:

show_list1(5, 'L', 0.5);

也就是说,args 被替换为三个存储在 args 中的值。因此,表示法 args… 展开为一个函数参数列表。不幸的是,该函数调用与原始函数调用相同,因此它将使用相同的参数不断调用自己,导致无限递归(这存在缺陷)。

在可变参数模板函数中使用递归

虽然前面的递归让 show_list1() 成为有用函数的希望破灭,但正确使用递归为访问参数包的内容提供了解决方案。这里的核心理念是,将函数参数包展开,对列表中的第一项进行处理,再将余下的内容传递给递归调用,以此类推,直到列表为空。与常规递归一样,确保递归将终止很重要。这里的技巧是将模板头改为如下所示:

template<typename T, typename... Args>
void show_list3( T value, Args... args)

对于上述定义,show_list3() 的第一个实参决定了 T 和 value 的值,而其他实参决定了 Args 和 args 的值。这让函数能够对 value 进行处理,如显示它。然后,可递归调用 show_list3(),并以 args… 的方式将其他实参传递给它。每次递归调用都将显示一个值,并传递缩短了的列表,直到列表为空为止。下面的程序提供了一种实现,它虽然不完美,但演示了这种技巧。

// variadic1.cpp -- using recursion to unpack a parameter pack
#include<iostream>
#include<string>// definition for 0 parameters -- terminating call
void show_list3() {}// definition for 1 or more parameters
template<typename T, typename... Args>
void show_list3( T value, Args... args) {std::cout << value << ", ";show_list3(args...);
}int main() {int n = 14;double x = 2.71828;std::string mr = "Mr. String objects!";show_list3(n, x);show_list3(x*x, '!', 7, mr);return 0;
}
  1. 程序说明
    请看下面的函数调用:

    show_list3(x*x, '!', 7, mr);
    

    第一个实参导致 T 为 double,value 为 x*x。其他三种类型(char、int 和 std::string)将放入 Args 包中,而其他三个值(‘!’, 7 和 mr)将放入 args 包中。

    接下来,函数 show_list3() 使用 cout 显示 value(大约为 7.38905)和字符串“,”。这完成了显示列表中第一项的工作。

    接下来是下面的调用:

    show_list3(args...);
    

    考虑到 args… 的展开作用,这与如下代码等价:

    show_list3('!', 7, mr);
    

    前面说过,列表将每次减少一项。这次 T 和 value 分别为 char 和 ‘!’,而余下的两种类型和两个值分别被包装到 Args 和 args 中,下次递归调用将处理这些缩小了的包。最后,当 args 为空时,将调用不接受任何参数的 show_list3(),导致处理结束。

    上面的程序的输出如下:

    14, 2.71828, 7.38905, !, 7, Mr. String objects!, 
    
  2. 改进
    可对 show_list3() 做两方面的改进。当前,该函数在列表的每项后面显示一个逗号,但如果能省区最后一项后面的逗号就好了。为此,可添加一个处理一项的模板,并让其行为与通用模板稍有不同:

    // definition for 1 parameter
    template<typename T>
    void show_list3(T value) {std::cout << value << '\n';
    }
    

    这样,当 args 包缩短到只有一项时,将调用这个版本,而它打印换行符而不是逗号。另外,由于没有递归调用 show_list3(),它也将终止递归。

    另一个可改进的地方是,当前的版本按值传递一切。对于这里使用的简单类型来说,这没问题,但对于 cout 可打印的大型类来说,这样做的效率很低。在可变参数模板中,可指定展开模式(pattern)。为此,可将下述代码:

    show_list3(Args... args);
    

    替换为如下代码:

    show_list3(const Args&... args);
    

    这将对每个函数参数应用模式 const&。这样,最后分析的参数将不是 std::string mr,而是 const std::string & mr。

    下面的程序包含这两项修改。

    // variadic2.cpp
    #include<iostream>
    #include<string>// definition for 0 parameters;
    void show_list() {}// definition for 1 parameter
    template<typename T>
    void show_list(const T& value) {std::cout << value << '\n';
    }// definition for 2 or more parameters
    template<typename T, typename... Args>
    void show_list(const T& value, const Args&... args) {std::cout << value << ", ";show_list(args...);
    }int main() {int n = 14;double x = 2.71828;std::string mr = "Mr. String objects!";show_list(n, x);show_list(x*x, '!', 7, mr);return 0;
    }
    

    该程序的输出如下:

    14, 2.71828
    7.38905, !, 7, Mr. String objects!
    

相关文章:

《C++ Primer Plus》第18章:探讨 C++ 新标准(6)

可变参数模板 可变参数模板&#xff08;variadic template&#xff09;让您能够创建这样的模板函数和模板类&#xff0c;即可接收可变数量的参数。这里介绍可变参数模板函数。例如&#xff0c;假设要编写一个函数&#xff0c;它可接受任意数量的参数&#xff0c;参数的类型只需…...

.Net Core中使用是SQL Server的邮件发送功能

.Net Core中使用是sqlserver的邮件发送功能准备需求启用SQL Server的电子邮件功能检查和测试在.net Core中调用在sqlsrver的管理中有一个数据库邮件功能,再此可以使用sqlserver来自动发送一些邮件,但是有一些需要插入附件的邮件则需要使用程序代码来解决,下面就是使用C#来调用s…...

Nginx优化服务和防盗链

Nginx优化服务和防盗链一、长连接1、修改主配置文件2、测试3、在主配置文件添加4、验证二、Nginx第三方模块1、开源的echo模块2、查看是否成功3、加echo模块步骤4、网页测试验证三、搭建虚拟主机1、编译安装好nginx后&#xff0c;对主配置文件进行修改2、创建文件3、验证四、防…...

B树与B+树

认识了解MySQL中的B树B树引出什么是B树什么是B树B树的优点B树引出 在MySQL中,如果我们设置了主键, 那么对于该列表中的数据就有了一个索引,插入表中数据的主键值不能重复,而且不能为空. 那当我们插入数据的时候, 它是如何通过索引来判断主键值是否重复的呢? 我们想到它肯定是…...

QEMU网络配置

文章目录1. 前言2. 测试环境3. 配置步骤3.1 host 配置3.1.1 检查 host 对 TUN/TAP 和 网桥的支持情况3.1.2 网桥一端的建立&#xff1a;创建网桥设备&#xff0c;并添加 host 网卡到网桥3.1.3 网桥另一端的建立&#xff1a;TUN/TAP 配置3.2 guest 端的配置4. 参考链接1. 前言 …...

windows安装tomcat

这里写自定义目录标题tomcat官网下载安装包并解压环境变量配置启动tomcat访问http://localhost:8080/修复启动出现乱码问题tomcat官网下载安装包并解压 环境变量配置 系统环境变量新增&#xff1a; 变量名&#xff1a;CATALINA_HOME 变量值&#xff1a;tomcat的安装目录 编辑…...

刷题记录:牛客NC23051华华和月月种树 树链剖分+离线加点

传送门:牛客 题目描述: 华华看书了解到&#xff0c;一起玩养成类的游戏有助于两人培养感情。所以他决定和月月一起种一棵树。因为华华现在也是信息学高手了&#xff0c;所以他们种的树是信息学意义下的。 华华和月月一起维护了一棵动态有根树&#xff0c;每个点有一个权值。刚…...

年薪20W软件测试工程师必备的6大技能(建议收藏)

软件测试 随着软件开发行业的日益发展&#xff0c;岗位需求量和行业薪资都不断增长&#xff0c;想要入行的人也是越来越多&#xff0c;但不知道从哪里下手&#xff0c;今天&#xff0c;就给大家分享一下&#xff0c;软件测试行业都有哪些必会的方法和技术知识点&#xff0c;作…...

【存储】RAID2.0+、多路径技术、磁盘可靠性技术

RAID2.0RAID 2.0技术RAID技术发展RAID 2.0软件逻辑对象RAID 2.0基本原理硬盘域Storage Pool & TierDisk Group&#xff08;DG&#xff09;LD&#xff08;逻辑磁盘&#xff09;Chunk&#xff08;CK&#xff09;Chunk Group&#xff08;CKG&#xff09;ExtentGrainVolume &am…...

Vue 2

文章目录1. 简介2. 第一个Vue程序3. 指令3.1 判断循环3.2 操作属性3.3 绑定事件3.4 表单中数据双向绑定3.5 其他内置指令3.6 自定义指令4. 组件4.1 全局注册4.2 局部注册4.3 组件通讯4.4 单文件组件5. 组件插槽5.1 单个插槽5.2 具名插槽5.3 作用域插槽6. 内置组件6.1 component…...

Ubuntu 安装 Docker Engine

【参考】Install Docker Engine on Ubuntu | Docker Documentation: https://docs.docker.com/engine/install/ubuntu/ 【参考】Docker CE 镜像源站-阿里云开发者社区 https://developer.aliyun.com/article/110806 【规范】模仿 Docker 文档&#xff0c;Ubuntu, Docker 首字母…...

SpringBoot入门 - 添加内存数据库H2

上文我们展示了通过学习经典的MVC分包结构展示了一个用户的增删查改项目&#xff0c;但是我们没有接入数据库&#xff1b;本文将在上文的基础上&#xff0c;增加一个H2内存数据库&#xff0c;并且通过Spring 提供的数据访问包JPA进行数据查询。准备知识点在介绍通过Spring JPA接…...

高质量数字化转型创新发展大会暨中国信通院“铸基计划”年度会议成功召开

2023年3月3日&#xff0c;由中国信通院主办的高质量数字化转型创新发展大会暨中国信通院“铸基计划”年度会议在北京成功召开。本次大会深度展示了中国信通院在数字化领域的工作成果&#xff0c;并全面展望了2023年行业的数字化发展趋势。同时&#xff0c;大会发布了中国信通院…...

2023年如何通过软考初级程序员?

初级的考试难度不大&#xff0c;稍微有点编程基础&#xff0c;认真备考应该没什么大问题。 先清楚大纲&#xff1a; 高效备考&#xff01;理清考点&#xff0c;针对性复习 科目一&#xff1a;综合知识 75道单项选择题&#xff0c;1题1分&#xff0c;时长150分钟&#xff1b;…...

视频自动播放的实现与问题解决

一、前言 页面加载一个视频并且自动播放,这个需求看起来非常简单,实现起来感觉也非常简单;但是,实际做起来还是有几处容易产生问题的地方卡住进度。本文讨论基于Vue3的项目在实现页面加载视频后的自动播放遇到的几个问题。 二、页面实现 页面实现非常简单。在页面上放置一个…...

ThreadLocal 理解及面试

一、ThreadLocal 引用关系 图解关系说明&#xff1a; 每个线程拥有自己的 ThreadLocalMap 属性&#xff1b;ThreadLocalMap 的存储结构为 Entry[] 数组&#xff1b;Entry的Key是ThreadLocal类型且弱引用指向ThreadLocal对象&#xff0c;Value是我们自己定义的泛型值对象&#…...

巾帼绽芬芳 一起向未来(中篇)

编者按&#xff1a;为了隆重纪念纪念“三八”国际妇女节113周年&#xff0c;快来与你全方位、多层次分享交流“三八”国际妇女节的前世今生。分上篇&#xff08;节日简介、节日发展和节日意义&#xff09;、中篇&#xff08;节日活动宗旨和世界各国庆祝方式&#xff09;和下篇&…...

Qt学习2-Qt Creator新建项目小tips(哔站视频学习记录)

放送两个小tips: 1、MinGW和MSVC的区别 QT学习笔记&#xff08;二&#xff09;&#xff1a;QT MinGW 和 MSVC 编译方式_Leon_Chan0的博客-CSDN博客 2、如何安装QT对应版本的MSVC (1)问题描述&#xff1a;Qt5.12.8支持MSVC2015和MSVC2017&#xff0c;但是系统安装的是Visual…...

React-高阶组件

认识高级组件 高阶函数的维基百科定义:至少满足以下条件之一 1、接受一个或多个函数作为输入; 2、输出一个函数; JavaScript中比较常见的 filter、map、reduce 都是高阶函数 那么说明是高阶组件呢? 高阶组件的英文是 Higher-Order Components&#xff0c;简称为 HOC;官方的…...

python学习——【第一弹】

前言 Python是一种跨平台的计算机程序设计语言&#xff0c;是ABC语言的替代品&#xff0c;属于面向对象的动态类型语言&#xff0c;最初被设计用于编写自动化脚本&#xff0c;随着版本的不断更新和语言新功能的添加&#xff0c;越来越多被用于独立的、大型项目的开发。 从这篇…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站&#xff0c;会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后&#xff0c;网站没有变化的情况。 不熟悉siteground主机的新手&#xff0c;遇到这个问题&#xff0c;就很抓狂&#xff0c;明明是哪都没操作错误&#x…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录

ASP.NET Core 是一个跨平台的开源框架&#xff0c;用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录&#xff0c;以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接&#xff1a;3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…...

有限自动机到正规文法转换器v1.0

1 项目简介 这是一个功能强大的有限自动机&#xff08;Finite Automaton, FA&#xff09;到正规文法&#xff08;Regular Grammar&#xff09;转换器&#xff0c;它配备了一个直观且完整的图形用户界面&#xff0c;使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

FFmpeg:Windows系统小白安装及其使用

一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】&#xff0c;注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录&#xff08;即exe所在文件夹&#xff09;加入系统变量…...

Chrome 浏览器前端与客户端双向通信实战

Chrome 前端&#xff08;即页面 JS / Web UI&#xff09;与客户端&#xff08;C 后端&#xff09;的交互机制&#xff0c;是 Chromium 架构中非常核心的一环。下面我将按常见场景&#xff0c;从通道、流程、技术栈几个角度做一套完整的分析&#xff0c;特别适合你这种在分析和改…...

【SpringBoot自动化部署】

SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一&#xff0c;能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时&#xff0c;需要添加Git仓库地址和凭证&#xff0c;设置构建触发器&#xff08;如GitHub…...