【C++11】可变参数的函数模板 的 定义 和 应用练习

文章目录

    • 定义
    • 常见应用
      • 模板参数包展开
      • 可变参数模板类
      • 可变参数模板函数重载
    • 练习
      • 打印任意数量的参数
      • 将任意数量的参数合并为一个字符串
      • 解包元组(Tuple Unpacking)
    • emplace_back
      • 和 push_back 的比较
      • 实例

定义

可变参数模板(Variable-length argument template)是C++11引入的特性之一,也称为“展开包”(Expansion Pack)。它允许我们在函数模板中使用可变数量的参数,这些 参数可以是任意类型 ,并且可以在函数模板中以类似于函数参数列表的方式进行使用。

使用可变参数模板时,需要在函数模板的参数列表中 使用省略号(…) 来表示可变数量的参数,并通过模板参数包展开来访问这些参数。

> 图片

它里面包含了 0到N(N>=0)个模版参数。我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数


常见应用

模板参数包展开

可变参数的 模板参数包可以通过展开 来实现模板的多态性。

  1. 递归展开
// 递归终止函数
template <class T>
void ShowList(const T& t)
{cout << t << endl;
}
// 展开函数
template <class T, class ...Args>
void ShowList(T value, Args... args)
{cout << value <<" ";ShowList(args...);
}
int main()
{ShowList(1);ShowList(1, 'A');ShowList(1, 'A', std::string("show"));return 0;
}

ShowList() 函数被定义为两个版本:一个递归终止函数和一个可变参数模板函数。递归终止函数只接受一个参数,并将它打印到标准输出中。可变参数模板函数接受一个参数和一个参数包,将第一个参数打印到标准输出中,并递归调用自身来处理参数包。

  1. 逗号表达式展开
template <class T>
void PrintArg(T t)
{// 打印模板参数cout << t << " ";
}
//展开函数
template <class ...Args>
void ShowList(Args... args)
{// 将可变参数展开成一个数组aint arr[] = { (PrintArg(args), 0)... };cout << endl;
}
int main()
{ShowList(1);ShowList(1, 'A');ShowList(1, 'A', std::string("sort"));return 0;
}

两次执行结果:

在这里插入图片描述

可变参数模板类

可变参数模板也可以用于定义类模板,如下图:

在这里插入图片描述

在这个例子中,我们定义了一个可变参数模板类 Tuple,它接受任意数量和类型的参数,并包含一个 std::tuple 类型的成员变量 values。在 main() 函数中,我们创建了一个包含整数、字符串和浮点数的元组,并将它赋值给 t 中的 values 成员变量。

可变参数模板函数重载

可变参数模板可以与函数重载一起使用,实现不同参数数量或类型的函数重载。例如:

template<class T>
void print(T t) {std::cout << t << std::endl;
}template<typename T, typename... Args>
void print(T t, Args... args) {std::cout << t << " ";print(args...);
}int main() {print(1, 2, 3, "hello", 'A'); // 输出 1 2 3 hello Areturn 0;
}

在这个例子中,我们定义了两个 print() 函数,一个接受单个参数,另一个接受任意数量的参数。在调用时,编译器会根据参数数量和类型来选择对应的函数进行调用。


练习

打印任意数量的参数

使用可变参数模板可以方便地打印任意数量的参数。例如,我们可以编写一个函数模板来打印任意数量的参数:

在这里插入图片描述

将任意数量的参数合并为一个字符串

使用可变参数模板可以方便地将任意数量的参数合并为一个字符串。例如,我们可以编写一个函数模板来将任意数量的字符串合并为一个字符串:

template<class ...Args>
std::string concat(Args... args) {// 创建一个stringstream对象ss,用于将多个参数拼接成一个字符串。std::stringstream ss;int arr[] = { (ss << args, 0)... }; // 折叠表达式,将所有参数写入 ss 中return ss.str();
}int main()
{// 执行下面代码string s = concat("hello", " ", "world", ".");cout << s << endl;return 0;
}

代码执行结果:(合并成功)

> 图片

解包元组(Tuple Unpacking)

使用可变参数模板可以方便地解包元组(Tuple)。例如,我们可以编写一个函数模板来计算元组中所有元素的和

// 解包元组(Tuple Unpacking)
template<class ...Args>
// 定义元组类
class Tuple 
{
public:std::tuple<Args...> values;
};int main() {Tuple<int, std::string, double> tp;tp.values = std::make_tuple(1, "hello", 1.14);std::cout << std::get<0>(tp.values) << " " << std::get<1>(tp.values) << " " << std::get<2>(tp.values) << std::endl;return 0;
}

程序执行结果:

在这里插入图片描述


emplace_back

C++11 中 STL容器引入了 emplace 相关接口函数,它支持模板的可变参数,和 万能引用

在这里插入图片描述
在这里插入图片描述

和 push_back 的比较

emplace_back()和push_back()都是向容器的尾部添加元素的函数,比如在C++中常用的std::vector容器。它们之间的主要区别在于元素的构造方式性能方面

  1. 构造方式
  • push_back()函数接受一个已经构造好的元素作为参数,然后将该元素的副本插入到容器中。这意味着在调用push_back()之前,必须先创建一个完整的元素对象,然后将其复制。
  • emplace_back()函数接受参数,并在容器内直接构造一个新的元素。它使用传递的参数来构造元素,而不是通过复制或移动现有的元素。因此,在调用emplace_back()时,可以直接传递构造该元素所需的参数,而无需提前创建完整的元素对象。
  1. 性能:
  • emplace_back()函数接受参数,并在容器内直接构造一个新的元素。它使用传递的参数来构造元素,而不是通过复制或移动现有的元素。因此,在调用emplace_back()时,可以直接传递构造该元素所需的参数,而无需提前创建完整的元素对象。
  • 相比之下,emplace_back()直接在容器内构造元素,并避免了额外的拷贝操作。这在大量添加元素时可以带来性能上的改进,尤其是对于复制构造代价较高的对象或容器。

实例

在这里插入图片描述

可以看出在使用上两者差别不大,主要是内部的具体过程

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

springcloud+docker+k8s发布安装第三方插件脚本

Dockerfile 安装第三方依赖插件软件 &#xff0c;以及构建镜像 #基础镜像&#xff0c;如果本地仓库没有&#xff0c;会从远程仓库拉取 openjdk:8 FROM docker.io/centos:centos7 #暴露端口 EXPOSE 9311 # 避免centos中文乱码 ENV LANG en_US.utf8 #容器中创建工作目录 WORKDIR …

MySQL的存储引擎与基本使用讲解

目录 一、前言 1.MySQL的介绍 二、存储引擎 1.什么是存储引擎 2.常见存储引擎 2.1.InnoDB(MySQL默认引擎) 2.1.1.四种隔离级别 2.2.MyISAM存储引擎 2.3.Memory存储引擎 3.ACID事务 三、CRUD操作 1.插入数据 2.查询数据 3.修改数据 4.删除数据 四、数据库 1.默认…

Matlab 校正镜头畸变图像

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 生活中一些针孔相机会给图像带来严重的失真,这主要是由于硬件和环境的因素所示导致的,其中主要的两种畸变是径向畸变和切向畸变。 径向畸变会导致直线看起来弯曲,即点距离图像中心越远,径向畸变就越大。例如,下…

RS485信号协议

在电子行业中&#xff0c;信号协议有很多&#xff0c;包括I2C&#xff0c;SPI&#xff0c;USB&#xff0c;UART&#xff0c;RS485等等&#xff0c;每个协议都有自己的特点和应用场景。 其中的RS485协议就是一种在电路板设计中非常常见的协议&#xff0c;是美国电子工业协会&…

k8s1.19使用ceph15 rbd块存储

一、ceph集群操作 #创建rbd #创建存储池,指定pg和pgp的数量, pgp是对存在于pg的数据进行组合存储,pgp通常等于pg的值 # 创建存储池 ceph osd pool create kubernetes 128 128#对存储池启用 RBD 功能 ceph osd pool application enable kubernetes rbd#通过 RBD 命令对存储池…

【微信小程序开发】第 9 课 - 小程序的协同工作和发布

欢迎来到博主 Apeiron 的博客&#xff0c;祝您旅程愉快 &#xff01; 时止则止&#xff0c;时行则行。动静不失其时&#xff0c;其道光明。 目录 1、协同工作 1.1、了解权限管理需求 1.2、了解项目成员的组织结构 1.3、小程序的开发流程 2、小程序成员管理 2.1、成员管…

解决uni-app微信小程序底部输入框,键盘弹起时页面整体上移问题

存在问题 做了一个记录页面&#xff08;类似单方聊天页&#xff09;&#xff0c;输入框在底部&#xff1b;当弹出键盘时&#xff0c;页面整体上移&#xff0c;页面头信息会消失不见 需要实现效果&#xff1a; 比如一个记录页面&#xff0c;需要在键盘弹出时&#xff1a; 底…

还在用策略模式解决 if-else?Map+函数式接口方法才是YYDS!

本文介绍策略模式的具体应用以及Map函数式接口如何 “更完美” 的解决 if-else的问题。 需求 最近写了一个服务&#xff1a;根据优惠券的类型resourceType和编码resourceId来 查询 发放方式grantType和领取规则 实现方式&#xff1a; 根据优惠券类型resourceType -> 确定查…

数据结构 | 二叉排序树

一、数据结构定义 /* 二叉排序树 */ typedef int TreeType; typedef struct BSTNode {TreeType data;struct BSTNode* lchild, * rchild; }*BSTree, BSTNode;二、方法概览 BSTNode* CreateTreeNode(TreeType data); // 创建二叉树结点 BSTNode* InsertTree(TreeType data, BS…

5个网站帮你找到免费优质的视频素材

5个免费可商用视频素材网站&#xff0c;建议收藏&#xff01; 潮点视频 https://shipin520.com/shipin-mb/all-def-267-all-all-all-all-all-0-1.html?from_code2510 潮点视频是一个提供优质高清、无水印的视频素材网站&#xff0c;站内有大量的AE模板、PR模板、实拍素材、视…

Dinky:问题总结

一、启动时指定flink版本&#xff0c;因为dinky本身也集成了部分flink ./auto.sh start 1.12 二、数据源管理新增mysql时的url jdbc:mysql://ip:3306/dinky?useUnicodetrue&characterEncodingutf8&useSSLfalse&autoReconnecttrue&failOverReadOnlyfalse 不要…

【Java高级语法】(十三)注解:解码程序设计中的元数据利器,在小小的@符里挖呀挖呀挖~用小小的注解做强大的开发...

Java高级语法详解之注解 1️⃣ 概念2️⃣ 优势和缺点3️⃣ 使用3.1 元注解3.2 自定义注解3.3 常用内置注解 4️⃣ 应用场景5️⃣ 底层原理6️⃣ 扩展&#xff1a;那些流行框架中的注解&#x1f33e; 总结 1️⃣ 概念 Java 注解&#xff08;Annotation&#xff09; 是Java语言中…