第18 章探讨 C++新标准.可变参数模板,模板和函数参数包,展开参数包

news2024/9/20 0:12:00

第18 章探讨 C++新标准.可变参数模板,模板和函数参数包,展开参数包

第18 章探讨 C++新标准.可变参数模板,模板和函数参数包,展开参数包

文章目录

  • 第18 章探讨 C++新标准.可变参数模板,模板和函数参数包,展开参数包
  • 18.6 可变参数模板
  • 18.6.1 模板和函数参数包
  • 18.6.2 展开参数包
  • 18.6.3 在可变参数模板函数中使用递归
  • 程序清单18.9 variadic1.cpp
    • 1.程序说明
    • 2.改进
  • 程序清单 18.10 variadic2.cpp


18.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.38905,!,7,Mr.String objects!
要创建可变参数模板,需要理解几个要点:

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

18.6.1 模板和函数参数包

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

template<typename T>
void show list0(T value)
std::cout << value s,";

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

show list0(2.15);

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

// Argsis a template parameter pack
template<typename...Args>
void show list1(Args... args)// args is a function parameter pack

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

show listl('S'80"sweet"4.5);

在这种情况下,参数包 Args 包含与函数调用中的参数匹配的类型:char、int、constchar *和 double.下面的代码指出 value 的类型为
T:

void show listo(T value)

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

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

更准确地说,这意味着函数参数包 args包含的值列表与模板参数包Args包含的类型列表匹配–无论是类型还是数量。在上面的示例中,args包含值’S’、80、“sweet”和 4.5。这样,可变参数模板 show listl()与下面的函数调用都匹配:

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”)。

18.6.2 展开参数包

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

template<typename...Args>/Args is a template parameter packvoid show listl(Args... args)// args is a function parameter pack
show listl(args...);//passes unpacked args to show list1()

这是什么意思呢?为何说它存在缺陷?假设有如下函数调用:

show list1(5,'0.5);

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

show listl(args...);

将展开成如下所示:

show list1(5,'L',0.5);

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

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

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

templatectypename T,typename...Args>
void show list3(T value, Args... args)

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

程序清单18.9 variadic1.cpp

//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(),导致处理结束。
程序清单18.9中两个函数调用的输出如下:
14,2.71828,7.38905,!,7,Mr.String objects!,

2.改进

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

//definition for i parameter
template<typename T>
void show iist3(T value)
std::cout << value << n';

这样,当 args 包缩短到只有一项时,将调用这个版本,而它打印换行符而不是逗号。另外,由于没有递归调用 show list3(),它也将终止递归。
另一个可改进的地方是,当前的版本按值传递一切。对于这里使用的简单类型来说,这没问题,但对于cout 可打印的大型类来说,这样做的效率很低。在可变参数模板中,可指定展开模式(pattem)。为此可将下述代码:

show list3(Args... args);

替换为如下代码:

show list3(const Argsk... args);

这将对每个函数参数应用模式const&。这样,最后分析的参数将不是std::stringmr,而是 conststd::string& mr.
程序清单18.10包含这两项修改。

程序清单 18.10 variadic2.cpp

// 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;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2050465.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Bootstrap个人技术博客响应式网页模板

Bootstrap个人技术博客响应式模板基于Bootstrap3.3.5制作&#xff0c;自适应分辨率&#xff0c;兼容PC端和移动端&#xff0c;全套模板&#xff0c;包括首页、关于、网页配色、内容页、友情链接、读者墙、标签云、点赞等网站模板页面。模板下载地址http://m.bokequ.com/moban/1…

我在高职教STM32——I2C通信之SHT20传感器(2)

大家好,我是老耿,高职青椒一枚,一直从事单片机、嵌入式、物联网等课程的教学。对于高职的学生层次,同行应该都懂的,老师在课堂上教学几乎是没什么成就感的。正是如此,才有了借助CSDN平台寻求认同感和成就感的想法。在这里,我准备陆续把自己花了很多心思设计的教学课件分…

ubuntu基于sealos搭建k8s集群,helm3安装配置自动化扩容Prometheus,grafana出图展示,以及动态web搭建

1.项目简介 大方向&#xff1a;k8s云原生方向&#xff0c;运维技术&#xff0c;配置问题解决 解决技术:ubuntu模板机安装&#xff0c;配置远程xshell连接ubuntu&#xff0c;设置静态ip&#xff0c;换ubuntu阿里云源&#xff0c;配置集群间域名解析&#xff0c;解决双IP冲突网…

I2C的10-bit地址空间

10-bit地址空间&#xff1a; I2C支持 10-bit的设备地址&#xff0c;此时的时序如下图所示&#xff1a; 在 10-bit地址的 I2C系统中&#xff0c;需要两个帧来传输 slave的地址。第一个帧的前 5个 bit固定为 b11110&#xff0c;后接 slave地址的高 2位&#xff0c;第 8位仍然是 …

牛客面经学习笔记(四)

这种拨码开关在PLC里面很是常用&#xff1a; 这种弧型线就很漂亮&#xff1a; 这个白色按键很漂亮&#xff1a; 快恢复保险丝&#xff1a; 继电器电路&#xff1a; 这里的续流二极管很重要&#xff0c;因为继电器是感性元件&#xff1a; 【【必考】5招搞清楚&#xff01;单点接…

关于pytorch后续学习需要下载的包太慢怎么办?tensorboard为例

启发&#xff1a; anaconda python3.7安装TensorFlow 1.9.0&#xff08;CPU版&#xff09; 和这个佬的 我是CPU版本&#xff08;好像是需要找anaconda对应版本&#xff0c;我不知道咋找版本&#xff0c;不知道不同版本的对应关系&#xff09; 此时&#xff0c;我们可以浅浅pip…

民航网上订票系统设计和实现--论文pf

TOC springboot427民航网上订票系统设计和实现--论文pf 第1章 绪论 1.1选题动因 当前的网络技术&#xff0c;软件技术等都具备成熟的理论基础&#xff0c;市场上也出现各种技术开发的软件&#xff0c;这些软件都被用于各个领域&#xff0c;包括生活和工作的领域。随着电脑和…

【系统分析师】-综合知识-系统架构

1、设计模式 1&#xff09;观察者模式定义了对象间的一种一对多依赖关系&#xff0c;使得每当一个对象改变状态&#xff0c;则所有依赖于它的对象都会得到通知并被自动更新【消息订阅】。在该模式中&#xff0c;发生改变的对象称为观察目标&#xff0c;被通知的对象称为观察者&…

泰坦尼克号 - 从灾难中学习机器学习/Titanic - Machine Learning from Disaster(kaggle竞赛)第二集(加载数据)

此次目的&#xff1a; hello大家好&#xff0c;俺是没事爱瞎捣鼓又分享欲爆棚的叶同学&#xff01;&#xff01;&#xff01;准备出几期博客来记录我学习kaggle数据科学入门竞赛的过程&#xff0c;顺便也将其中所学习到的知识分享出来。这是第一集&#xff08;了解赛题&#x…

Ansible可视化管理之web界面集成使用探究(未完待续)

一、前言 因某集成商管理的客户资源涉及4A接入管控要求&#xff0c;其中密码必须3个月更新一次&#xff0c;随着纳管主机的数量增多&#xff0c;手动去修改密码变得不现实&#xff0c;考虑无侵入性和资源耗用&#xff0c;便捷性等因素&#xff0c;首先选用Ansible作为此需求的…

武汉君耐营销策划有限公司员工信息管理系统pf

TOC springboot428武汉君耐营销策划有限公司员工信息管理系统pf 第1章 绪论 1.1 研究背景 互联网概念的产生到如今的蓬勃发展&#xff0c;用了短短的几十年时间就风靡全球&#xff0c;使得全球各个行业都进行了互联网的改造升级&#xff0c;标志着互联网浪潮的来临。在这个…

【Harmony OS 4.0】待办列表案例

src/main/ets/example1/Models.ets // 定义class类数据模型 export class TaskDataModel {// private 私有属性&#xff0c;在类对象外不允许随意更改数据&#xff0c;必须本地初始化。private tasks: Array<string> [早起晨练, 准备早餐, 阅读名著, 学习ArkTs, 玩游戏…

答题情况和每题得分

文章目录 1.提交答题情况1.PracticeDetailController.java2.PracticeDetailService.java3.PracticeDetailServiceImpl.java4.PracticeDetailDao.java5.PracticeDetailDao.xml6.reqSubmitSubjectDetailReq.java 7.dto1.SubjectDetailDTO.java2.SubjectDTO.java3.SubjectOptionDT…

算法的学习笔记—合并两个排序的链表(牛客JZ25)

&#x1f600;前言 在算法面试中&#xff0c;链表问题是经常遇到的考点之一&#xff0c;其中合并两个排序链表是一个非常经典的问题。本文将详细介绍如何通过递归和迭代两种方式实现两个有序链表的合并。 &#x1f3e0;个人主页&#xff1a;尘觉主页 文章目录 &#x1f600;合并…

大厂高频软件测试面试题和答案都帮你准备好啦,备战金九银十

还有两周时间就是金九银十求职招聘季了&#xff0c;每年的9月和10月&#xff0c;都是毕业生求职的大好时机&#xff0c;很多企业为招揽人才&#xff0c;会在每年的九十月份举办针对应届生的招聘会。接下来小编已为大家备好了多家大厂高频软件测试面试题和答案&#xff0c; 说下…

Zookeeper应用场景实战一

目录 1. Zookeeper Java客户端实战 1.1 Zookeeper 原生Java客户端使用 ZooKeeper常用构造器 示例代码&#xff1a; Zookeeper主要方法 1.2 Curator开源客户端使用 引入依赖 示例代码&#xff1a; 创建一个客户端实例 创建节点 一次性创建带层级结构的节点 获取数据 …

RTC时钟测试

1. 基础知识 Linux 的系统时间有时跟硬件时间是不同步的。 Linux时钟分为系统时钟(System Clock)和硬件(Real Time Clock&#xff0c;简称RTC)时钟。系统时钟是指当前Linux Kernel中的时钟&#xff0c;而硬件时钟则是主板上由电池供电的时钟&#xff0c;这个硬件时钟可以在BIO…

编译aws并访问minio

Aws amazon (S3) 是一个公开的服务&#xff0c;Web 应用程序开发人员可以使用它存储数字资产&#xff0c;包括图片、视频、音乐和文档。 S3 提供一个 RESTful API 以编程方式实现与该服务的交互. MinIO是兼容AWS SDK,所以可以通过aws访问minio文件系统。 指导文档&#xff1…

CSS知识点详解:display+float

display&#xff1a;浮动 1.block&#xff1a;使元素呈现为块级元素&#xff0c;可设置宽高 display: block; 特点&#xff1a;使元素呈现为块级元素&#xff0c;即该元素会以新行开始&#xff0c;占据整行的宽度&#xff0c;即使其宽度未满。 例子&#xff1a; 2.inline&a…

队列(笔记)

文章目录 1. 概念2. 实现方式3. 复杂度其他 4. 实际应用5. 内容出处 1. 概念 队列&#xff1a;其实就是排队。像我们在银行窗口取钱、车站买车票等都可以叫队列。 特点&#xff1a;队列只允许在后端(rear)进行插入操作&#xff0c;在前端(front)进行删除操作(即先进先出…