C++11 -- 可变参数模板

news2024/10/6 14:27:44

文章目录

  • 可变参数模板的概念
  • 可变模板参数的定义
  • 获取可变模板参数包的值
    • 递归函数方式展开参数包获取
    • 逗号表达式展开参数包获取
  • STL容器中的emplace相关接口参数
    • emplace_back与STL容器中的push_back的主要区别
    • emplace_back与push_back的差异原理
    • emplate_back与push_back的区别验证

可变参数模板的概念

( 1 ): C++98/01中,类模板和函数模板只能包含固定数量的模板参数.

( 2 ): 相比于C++98/01,C++11的新特性可变模板参数模板能够让我们创建可以接受可变参数的函数模板和类模板.

可变模板参数的定义

以下就是一个基本可变参数的函数模板.

//Args是一个模板参数包,args是一个函数形参参数包.
//声明一个函数形参参数包Args...args,其中这个形参参数包中可以包含0到任意个模板参数.
template < class ...Args >
void ShouList( Args ...args )
{}

注意:
上面的参数args前面有省略号,所以它就是一个可变模板参数,我们将带省略号的参数称为"参数包",它里面包含了0到N个(N>>0)个模板参数.

此外,我们也可以在函数模板内中使用sizeof计算函数形参参数包中参数的个数.

template <class ...Args >
void ShouwList( Args... args)
{
	cout << sizeof...( args ) << endl;
}
int main()
{
	string str("zh");

	ShouwList('A', str);           //2
	
	ShouwList(1, 'A', str);        //1

}

但是,C++11语法并不支持args[i]这样方式获取可变参数.

//编译无法通过.
template<class ...Args>
void ShowList(Args... args)
{
		for (int i = 0; i < sizeof...(args); i++)
	{
		cout << args[i] << " "; //使用args[i]打印参数包中的每个参数
	}
	cout << endl;
}

获取可变模板参数包的值

由于我们无法直接获取函数形参参数包args中的每个参数,只能通过展开参数包的方式获取参数包中的每一个参数,这是可变模板参数的一个主要特点,也是最大的难点.但是,我们可以通过一些特殊的办法来获取函数形参参数包的值.

递归函数方式展开参数包获取

( 1 ) :我们的本意是想通过函数递归的方式,将函数参数包每次调用ShouwList时都分为参数类型T,和参数包(个数-1)的形式,并将此时的函数参数类型T的参数值t打印出来.

( 2 ): 当函数参数包的参数个数为0时,此时与原来的ShouwList函数中的两个形参并不匹配,这时便不能继续递归了.此时,类似与我们以前写递归的结束条件一样,我们需要额外再写一个递归终止函数,随后编译器就会递归调用该函数.

//递归终止函数.
void ShouwList ( )
{
	cout << t << endl;
}

//利用递归将函数形参参数包展开.
template <class T,class  ...Args >
void ShouwList( T value,Args... args  )
{
	cout << value << " ";
	ShouwList(args...);
}
int main()
{ 
	ShouwList(1);                    //1 
	 
	ShouwList(1, 'A');              // 1, A

	ShouwList(1, 'A', std::string("sort")); // 1, A,sort
}

逗号表达式展开参数包获取

这种展开参数包的方式,不需要通过递归终止函数,而是在expand函数体中展开的.

  • 其中PrintArg是一个处理参数包中每一个参数的函数,每一次处理即对每一种参数t的打印.
  • 我们知道,逗号表达式中会按照顺序执行逗号前面的表达式.并且,expand函数中的逗号表达式:(PrintArg(args),0)也是按照这个顺序执行,先执行PrintArg(args),然后,再得到该一次逗号表达式的结果为0.
  • 同时,该处运用到了C++11的另外一个特性–初始化列表,即通过初始化列表来初始化一个变长的数组,{( PrintArg(args),0)…}将会展开成((PrintArg(args1),0),(PrintArg(args2),0),( PrintArg(args3),0),etc…),这代表每次展开一个逗号表达式args中参数包的个数就减少一个,最终,会创建一个元素值都为0的数组arr[sizeof…(args)].
  • 于是,在使用初始化列表和逗号表达式构造数组的时候,已经默认将args参数包中的参数都展开了.
//将每次的函数形参进行打印.
template < class T > 
void PrintArg(T t)
{
	cout << t << " ";
}
//利用递归将函数形参参数包展开.
template <class ...Args >
void ShouwList(Args... args)
{
	int arr[] = { (PrintArg(args),0)... };
	cout << endl;
}
int main()
{ 
	ShouwList(1);                    //1 
	 
	ShouwList(1, 'A');              // 1, A

	ShouwList(1, 'A', std::string("sort")); // 1, A,sort

	return 0;
}

小结:
可变模板参数一般用于需要传递多个形参的函数.

STL容器中的emplace相关接口参数

emplace_back与STL容器中的push_back的主要区别

如果插入实参为int,char等内置类型,那么push_back与emplace_back没有区别.
在这里插入图片描述

可是,如果我们存储的数据类型为自定义类型,例如:pair,区别如下.

  • 使用push_back插入数据时,只允许传递一个实参.
  • 使用emplace_back插入数据时,可以允许传递多个实参.
#include <vector>
int main()
{ 
	vector<pair< std::string,int>> v1;

	v1.push_back(make_pair("sort", 1));

	v1.emplace_back("sort", 1);
	
	return 0;
}

emplace_back与push_back的差异原理

  • emplace_back指:当需要传递多个实参时,emplace_back利用将参数包展开的方式提出来,然后直接对vector中pair进行构造.
  • push_back指:先利用传过来的一个实参构造pair,然后再利用构造出来的pair去构造vector中的pair.(如果构造出来的pair为左值,那就是拷贝构造,如果构造的为右值,那就是移动构,具体看vector中的具体实现).
    在这里插入图片描述

emplate_back与push_back的区别验证

在vector中,使用emplace_back和push_back传递多个实参调用时没有区别,都是调用一次构造加一次拷贝构造,这种为特殊情况,具体要看vector中的emplace_back的源码.
在这里插入图片描述

但是对于STL中其他容器来说,例如:list.在相同情况下,emplace_black相比于push_black来说少调用了一次拷贝构造.
在这里插入图片描述

总结:
综合以上情况,我们在插入数据时尽量使用emplace_back,进而更加高效.

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

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

相关文章

图像算法工程师岗位的主要职责(合集)

图像算法工程师岗位的主要职责 一、确定岗位的职责 1.根据工作任务的需要确立工作岗位名称及其数量; 2.根据岗位工种确定岗位职务范围; 3.根据工种性质确定岗位使用的设备、工具、工作质量和效率; 4.明确岗位环境和确定岗位任职资格; 5.确定各个岗位之间的相互关系; 6.根据岗位…

css元素的显示和隐藏

1. display显示隐藏 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><st…

测试C#分词工具jieba.NET

jieba.NET是jieba中文分词的C#版本&#xff0c;后者是优秀的Python中文分词组件GitHub中得到超过3万星。jieba.NET支持中文分词、关键词提取、词性标注等功能&#xff0c;本文主要测试其中文分词的功能基本用法。   新建测试项目&#xff0c;在NuGet管理器中添加jieba.NET。 …

SpringBoot --- 运维篇

一、打包与运行 1.1、程序打包与运行&#xff08;Windows版&#xff09; 所谓打包指将程序转换成一个可执行的文件&#xff0c;所谓运行指不依赖开发环境执行打包产生的文件。 SpringBoot程序是基于Maven创建的&#xff0c;在Maven中提供有打包的指令&#xff0c;叫做packag…

虹科HiveMQ与MQTT:构建互联汽车的新架构

前言 随着汽车的互联程度越来越高&#xff0c;汽车制造商和互联汽车平台提供商通过使用物联网技术&#xff0c;提供新服务并从车辆收集有价值的遥测数据&#xff0c;以此来增加营收。从高效的车队管理和汽车共享到预测性维护和高级驾驶员辅助系统&#xff0c;未来移动出行的可…

Packet Tracer – 访问控制列表演示

Packet Tracer – 访问控制列表演示 拓扑图 目标 第 1 部分&#xff1a;验证本地连接和测试访问控制列表 第 2 部分&#xff1a;删除访问控制列表和重复测试 背景信息 在本练习中&#xff0c;您将观察如何使用访问控制列表 (ACL) 阻止 ping 访问远程网络上的主机。 从配置…

大数据Doris(二十七):Broker Load导入HDFS数据到Doris表

文章目录 Broker Load导入HDFS数据到Doris表 一、创建Doris表 二、准备HDFS数据 三、准备Broker Load语句

【Springcloud】Feign远程调用

文章目录 1、RestTemplate远程调用2、基于Feign远程调用3、Feign的自定义配置4、Feign性能优化5、Feign的最佳实践思路6、案例 1、RestTemplate远程调用 在说基于Feign远程调用之前&#xff0c;先看一下RestTemplate发起远程调用的问题&#xff1a; String url "http:…

船新SpringBoot 3.1正式发布,新特性真香

Spring Boot 3.1 正式发布 Spring Boot 3.0 发布半年左右&#xff0c;Spring Boot 3.1 正式发布了&#xff1a; 同时发布更新的还有 2.7.x 版本&#xff0c;同时&#xff0c;2.6.x 版本线已经停止维护了&#xff0c;最新支持版本如下图所示&#xff1a; 2.7.x 这也是目前唯一正…

Python条件判断

目录 1. 语法 2. 常见用法 2.1. 比较符判断 2.2. 逻辑符判断 2.3. 成员符判断 2.4. 三目运算 1. 语法 记住3个关键字&#xff1a;if&#xff08;表示如果&#xff09;、elif&#xff08;再如果&#xff09;、else&#xff08;否则&#xff09; if [条件1]&#xff1…

数据链路层概述

1.数据链路层概述 笔记来源&#xff1a; 湖科大教书匠&#xff1a;数据链路层概述 声明&#xff1a;该学习笔记来自湖科大教书匠&#xff0c;笔记仅做学习参考 1.1 数据链路层在网络体系结构中的地位 主机具有体系结构的各个层次&#xff0c;路由器只需具有体系就够的最下面三…

chatgpt赋能python:Python“+”-了解Python中最常用的运算符

Python “” - 了解Python中最常用的运算符 Python是一种高级编程语言&#xff0c;用于开发各种应用程序&#xff0c;从Web应用和桌面应用到数据分析和科学计算。Python是一种简单易学&#xff0c;可读性强的语言&#xff0c;拥有丰富的库和工具。 在Python中&#xff0c;“”…

桥接模式解密:跨越鸿沟,桥接抽象与实现

一、概要 桥接模式&#xff08;Bridge Pattern&#xff09;是一种结构型设计模式&#xff0c;它将抽象部分与实现部分分离&#xff0c;使它们都可以独⽴的变化。其核心思想就是解耦&#xff0c;在面向对象编程中&#xff0c;抽象和实现是通过继承关系来实现的&#xff0c;但这…

《C和指针》读书笔记(第八章 数组)

目录 0 内容简介1. 一维数组1.1 数组名1.2 下标引用1.3 指针与下标1.4 指针的效率1.5 数组和指针1.6 作为函数参数的数组名1.7 声明数组参数1.8 初始化1.9 不完整的初始化1.10 自动计算数组长度1.11 字符数组的初始化 2. 多维数组2.1 存储顺序2.2 数组名2.3 下标2.4 指向数组的…

爆火出圈的chatGPT,到底是什么东东?

爆火出圈的chatGPT&#xff0c;到底是什么东东&#xff1f; 前言 2022年 11 月 30 日&#xff0c;OpenAI 实验室创始人兼 CEO Sam Altman 宣布发布聊天机器人模型&#xff1a;ChatGPT。ChatGPT 可以模仿人类的说话风格回答问题。很快&#xff0c;ChatGPT 火爆社交圈&#xff…

Linux音频和视频命令速查表

在Linux系统中&#xff0c;有许多命令可以帮助我们处理音频和视频文件&#xff0c;从基本的播放和转码&#xff0c;到编辑和处理音频、视频流。 本文将提供一个Linux音频和视频命令速查表&#xff0c;帮助您快速查找并了解各种常用的命令及其用法。 音频命令 播放音频文件 a…

堆和堆排序

目录 堆的概念 堆的实现 堆的存储结构 堆的插入操作 堆的删除操作 堆的创建 向上调整建堆和向下调整建堆 堆排序 堆的应用 - topK问题 堆的概念 “堆”是计算机科学中一种数据结构&#xff0c;可以看作是一棵完全二叉树。通常满足堆的性质&#xff1a;父节点的值总是…

【Linux】进程控制 — 进程程序替换 + 实现简易shell

文章目录 &#x1f4d6; 前言1. 进程程序替换1.1 程序替换的概念&#xff1a;1.2 为什么要程序替换&#xff1a;1.3 程序替换的原理&#xff1a; 2. 六个exec替换函数2.1 execl函数&#xff1a;2.2 execv函数&#xff1a;2.3 execlp函数&#xff1a;2.4 execvp函数&#xff1a;…

chatgpt赋能python:Python[:2]——简介和应用

Python [:2]——简介和应用 Python [:2]是一种流行的编程语言&#xff0c;其简单易用的语法使其成为许多人的首选编程语言之一。Python [:2]的迅速增长已经超越了其他编程语言&#xff0c;并且它正在成为各行各业中最有前途的编程语言之一。 Python 基础 Python [:2]的语法非…

MSQL系列(三) Mysql实战-索引最左侧匹配原则原理

Mysql实战-索引最左侧匹配原则原理 前面我们讲解了索引的存储结构&#xff0c;我们知道了BTree的索引结构&#xff0c;索引的叶子节点是严格排序的&#xff0c;就像你看到的 底层叶子节点 15->18->20->30->49->50等等 这样做有什么好处呢&#xff1f; 这就引出…