【C++11】可变参数模版

news2025/2/7 20:15:59

目录

一、可变参数模版

1.1 基本语法及原理 

1.2 包拓展

1.3 empalce系列接口


一、可变参数模版

之前我们在C语言中就学过可变参数,但是模版类型是固定的,怎么变呢?这里c++11就给出了可变参数模版

1.1 基本语法及原理 

C++11支持可变参数模板,也就是说支持可变数量参数的函数模板和类模板,可变数目的参数被称为参数包,存在两种参数包:模板参数包,表示零或多个模板参数;函数参数包:表示零或多个函数参数

  • template <class ...Args> void Func(Args... args) {}
  • template <class ...Args> void Func(Args&... args) {}
  • template <class ...Args> void Func(Args&&... args) {}

我们用省略号来指出一个模板参数或函数参数的表示一个包,在模板参数列表中,class...或
typename...指出接下来的参数表示零或多个类型列表;在函数参数列表中,类型名后面跟...指出接下来表示零或多个形参对象列表;函数参数包可以用左值引用或右值引用表示,跟前面普通模板一样,每个参数实例化时遵循引用折叠规则

可变参数模板的原理跟模板类似,本质还是去实例化对应类型和个数的多个函数。这里我们可以使用sizeof...运算符去计算参数包中参数的个数 

举个栗子:

	//注意...是0~n个参数
template <class ...Args>
void Print(Args&&... args)
{
	cout << sizeof...(args) << endl;
}
int main()
{
	double x = 2.2;
	Print(); // 包里有0个参数
	Print(1); // 包里有1个参数
	Print(1, string("xxxxx")); // 包里有2个参数
	Print(1.1, string("xxxxx"), x); // 包里有3个参数
	return 0;
}

可以理解为模版的模版,如果没有模版支持,我们就要写四个函数

总结:

  • 模版:一个函数模版实例出多个不同类型参数的函数
  • 可变参数模版:一个函数模版实例出多个不同个数参数的模版函数

1.2 包拓展

对于一个参数包,我们除了能计算他的参数个数,我们能做的唯一的事情就是扩展它,当扩展一个包时,我们还要提供用于每个扩展元素的模式,扩展一个包就是将它分解为构成的元素,对每个元素应用模式,获得扩展后的列表。我们通过在模式的右边放一个省略号(...)来触发扩展操作。底层的实现细节如图所示。

C++还支持更复杂的包扩展,直接将参数包依次展开依次作为实参给一个函数去处理。


但是不支持向数组一样使用

template <class ...Args>
void Print(Args... args)
{
// 可变参数模板编译时解析
// 下面是运行获取和解析,所以不支持这样用
	cout << sizeof...(args) << endl;
	for (size_t i = 0; i < sizeof...(args); i++)
	{
		cout << args[i] << " ";
	}

	cout << endl;
}


所以要这样去写

//包扩展
void ShowList()
{
	// 编译器时递归的终止条件,参数包是0个时,直接匹配这个函数
	cout << endl;
}
template <class T, class ...Args>
void ShowList(T x, Args&&... args)
{
	cout << x << " ";
	// args是N个参数的参数包
	// 调用ShowList,参数包的第一个传给x,剩下N-1传给第二个参数包
	ShowList(args...);
}
// 编译时递归推导解析参数
template <class ...Args>
void Print(Args&&... args)
{
	ShowList(args...);
}

int main()
{
	double x = 2.2;
	Print(); // 包里有0个参数
	Print(1); // 包里有1个参数
	Print(1, string("xxxxx")); // 包里有2个参数
	Print(1.1, string("xxxxx"), x); // 包里有3个参数
	return 0;
}

1.3 empalce系列接口

template <class... Args> void emplace_back (Args&&... args);

template <class... Args> iterator emplace (const_iterator position,Args&&... args);

C++11以后STL容器新增了empalce系列的接口,empalce系列的接口均为模板可变参数,功能上兼容push和insert系列,但是empalce还支持新玩法,假设容器为container<T>,empalce还支持直接插入构造T对象的参数,这样有些场景会更高效一些,可以直接在容器空间上构造T对象。

emplace_back总体而言是更高效,推荐以后使用emplace系列替代insert和push系列

第二个程序中我们模拟实现了list的emplace和emplace_back接口,这里把参数包不段往下传递,最终在结点的构造中直接去匹配容器存储的数据类型T的构造,所以达到了前面说的empalce支持直接插入构造T对象的参数,这样有些场景会更高效一些,可以直接在容器空间上构造T对象。

传递参数包过程中,如果是Args&&... args 的参数包,要用完美转发参数包,方式如下std::forward<Args>(args)... ,否则编译时包扩展后右值引用变量表达式就变成了左值。

#include<list>
// emplace_back总体而言是更高效,推荐以后使用emplace系列替代insert和push系列
int main()
{
	list<bit::string> lt;
	// 传左值,跟push_back一样,走拷贝构造
	bit::string s1("111111111111");
	lt.emplace_back(s1);
	cout << "*********************************" << endl;

	// 右值,跟push_back一样,走移动构造
	lt.emplace_back(move(s1));
	cout << "*********************************" << endl;

	// 直接把构造string参数包往下传,直接用string参数包构造string
	// 这里达到的效果是push_back做不到的
	lt.emplace_back("111111111111");
	cout << "*********************************" << endl;

	list<pair<bit::string, int>> lt1;
	// 跟push_back一样
	// 构造pair + 拷贝/移动构造pair到list的节点中data上
	pair<bit::string, int> kv("苹果", 1);
	lt1.emplace_back(kv);
	cout << "*********************************" << endl;

	// 跟push_back一样
	lt1.emplace_back(move(kv));
	cout << "*********************************" << endl;

	
	// 直接把构造pair参数包往下传,直接用pair参数包构造pair
	// 这里达到的效果是push_back做不到的
	lt1.emplace_back("苹果", 1);
	cout << "*********************************" << endl;
	return 0;
}

总结:emplace系列兼容push系列和insert的功能:

        部分场景下emplace可以直接构造,push和insert是构造+移动构造或构造+拷贝构造所以emplace综合而言更好用更强大,推荐用emplace系列替代push和insert


// List.h
namespace bit
{
	template<class T>
	struct ListNode
	{
		ListNode<T>* _next;
		ListNode<T>* _prev;
		T _data;
		ListNode(T&& data)
			:_next(nullptr)
			, _prev(nullptr)
			, _data(move(data))
		{}
		template <class... Args>
		ListNode(Args&&... args)
			: _next(nullptr)
			, _prev(nullptr)
			, _data(std::forward<Args>(args)...)
		{}
	};
	template<class T, class Ref, class Ptr>
	struct ListIterator
	{
		typedef ListNode<T> Node;
		typedef ListIterator<T, Ref, Ptr> Self;
		Node* _node;
		ListIterator(Node* node)
			:_node(node)
		{}
		// ++it;
		Self& operator++()
		{
			_node = _node->_next;
			return *this;
		}
		Self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		Ref operator*()
		{
			return _node->_data;
		}
		bool operator!=(const Self& it)
		{
			return _node != it._node;
		}
	};
	template<class T>
	class list
	{
		typedef ListNode<T> Node;
	public:
		typedef ListIterator<T, T&, T*> iterator;
		typedef ListIterator<T, const T&, const T*> const_iterator;
		iterator begin()
		{
			return iterator(_head->_next);
		}
		iterator end()
		{
			return iterator(_head);
		}
		void empty_init()
		{
			_head = new Node();
			_head->_next = _head;
			_head->_prev = _head;
		}
		list()
		{
			empty_init();
		}
		void push_back(const T& x)
		{
			insert(end(), x);
		}
		void push_back(T&& x)
		{
			insert(end(), move(x));
		}
		iterator insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;
			Node* newnode = new Node(x);
			Node* prev = cur->_prev;
			// prev newnode cur
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
			return iterator(newnode);
		}
		iterator insert(iterator pos, T&& x)
		{
			Node * cur = pos._node;
			Node* newnode = new Node(move(x));
			Node* prev = cur->_prev;
			// prev newnode cur
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
			return iterator(newnode);
		}
		template <class... Args>
		void emplace_back(Args&&... args)
		{
			insert(end(), std::forward<Args>(args)...);
		}
		// 原理:本质编译器根据可变参数模板生成对应参数的函数
		/*void emplace_back(string& s)
		{
		insert(end(), std::forward<string>(s));
		}
		void emplace_back(string&& s)
		{
		insert(end(), std::forward<string>(s));
		}
		void emplace_back(const char* s)
		{
		insert(end(), std::forward<const char*>(s));
		}
		*/
		template <class... Args>
		iterator insert(iterator pos, Args&&... args)
		{
			Node* cur = pos._node;
			Node* newnode = new Node(std::forward<Args>(args)...);
			Node* prev = cur->_prev;
			// prev newnode cur
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
			
			return iterator(newnode);
		}
	private:
		Node* _head;
	};
}

本篇完,下篇继续!

 

 

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

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

相关文章

FFmpeg在python里推流被处理过的视频流

链式算法处理视频流 视频源是本地摄像头 # codinggbk # 本地摄像头直接推流到 RTMP 服务器 import cv2 import mediapipe as mp import subprocess as sp# 初始化 Mediapipe mp_drawing mp.solutions.drawing_utils mp_drawing_styles mp.solutions.drawing_styles mp_holis…

我的秋招总结

我的秋招总结 个人背景 双非本&#xff0c;985硕&#xff0c;科班 准备情况 以求职为目的学习Java的时间大概一年。 八股&#xff0c;一开始主要是看B站黑马的八股文课程&#xff0c;背JavaGuide和小林coding还有面试鸭。 算法&#xff0c;250&#xff0c;刷了3遍左右 项目&…

C++:单例模式

创建自己的对象&#xff0c;同时确保对象的唯一性。 单例类只能有一个实例☞静态成员static☞静态成员 必须类外初始化 单例类必须自己创建自己的唯一实例 单例类必须给所有其他对象提供这一实例 静态成员类内部可以访问 构造函数私有化☞构造函数私有外部不能创建&#x…

RBTree(红黑树)

目录 红黑树的概念 红黑树的性质 红黑树节点的定义 红黑树的插入 1. 按照二叉搜索的树规则插入新节点 2. 检测新节点插入后&#xff0c;红黑树的性质是否造到破坏 红黑树的检测 红黑树的删除 红黑树和AVL树的比较 红黑树的概念 红黑树&#xff0c;是一种二叉搜索树&…

linux自动化一键批量检查主机端口

1、准备 我们可以使用下面命令关闭一个端口 sudo iptables -A INPUT -p tcp --dport 端口号 -j DROP我关闭的是22端口&#xff0c;各位可以关其它的或者打开其它端口测试&#xff0c;谨慎关闭22端口&#xff01;不然就会像我下面一样握手超时&#x1f62d;&#x1f62d;&…

Python大数据可视化:基于python大数据的电脑硬件推荐系统_flask+Hadoop+spider

开发语言&#xff1a;Python框架&#xff1a;flaskPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录 管理员功能界面 价格区间界面 用户信息界面 品牌管理 笔记本管理 电脑主机…

AEO海关认证的注意事项

AEO海关认证的注意事项繁多且至关重要&#xff0c;企业需细致准备&#xff0c;确保万无一失。 首先&#xff0c;企业需深入研读相关政策文件&#xff0c;如《中华人民共和国海关注册登记和备案企业信用管理办法》及《海关高级认证企业标准》&#xff0c;以政策为指引&#xff0…

MySQL如何只取根据某列连续重复行的第一条记录

前言 MySQL如何只取根据某列连续重复行的第一条记录&#xff0c;条件&#xff1a;某列、连续、验重 建表准备 DROP TABLE IF EXISTS test; CREATE TABLE test (id bigint NOT NULL,time datetime NULL DEFAULT NULL,price int NULL DEFAULT NULL,PRIMARY KEY (id) USING BT…

c++编译过程初识

编译过程 预处理&#xff1a;主要是执行一些预处理指令&#xff0c;主要是#开头的代码&#xff0c;如#include 的头文件、#define 定义的宏常量、#ifdef #ifndef #endif等条件编译的代码&#xff0c;具体包括查找头文件、进行宏替换、根据条件编译等操作。 g -E example.cpp -…

JS中的闭包和上下文

变量提升 和 函数提升 这里要提到一个提升的概念&#xff0c;即在JS中&#xff0c;在解析代码之前还有一个预处理的过程&#xff0c;这个过程中会把部分变量和函数声明提前到代码的最顶部&#xff0c; 会在其他所有代码之前执行。虽然当我们按照规范&#xff08;严格模式或者T…

GitLab 将停止为中国区用户提供服务,60天迁移期如何应对? | LeetTalk Daily

“LeetTalk Daily”&#xff0c;每日科技前沿&#xff0c;由LeetTools AI精心筛选&#xff0c;为您带来最新鲜、最具洞察力的科技新闻。 GitLab作为一个广受欢迎的开源代码托管平台&#xff0c;近期宣布将停止服务中国大陆、澳门和香港地区的用户提供服务。根据官方通知&#x…

【2024年最新】BilibiliB站视频动态评论爬虫

废话不多说&#xff0c;直接先放git仓库&#xff1a;GitHub - linyuye/Bilibili_crawler: bilibili爬虫&#xff0c;基于selenium获取oid与cookie&#xff0c;request获取api内容 〇&#xff1a;概念简述 oid&#xff1a;视频/动态的uuid&#xff0c;b站对于发布内容的通用唯…

汽车IVI中控开发入门及进阶(46):FFmpeg

概述: FFmpeg 是领先的多媒体框架,能够解码、编码、 转码、复用、解复用、流、过滤和播放 几乎所有人类和机器创建的东西。它支持最模糊的古老格式,直到最前沿。无论它们是由某个标准委员会、社区还是公司设计的。它还具有高度的可移植性:FFmpeg 在各种构建环境、机器架构…

计算属性 简写和 完整写法

计算属性渲染不加上括号 methods方法和computed属性区别&#xff1a; computed只计算一次&#xff0c;然后缓存&#xff0c;后续直接拿出来使用&#xff0c;而methods每次使用每次计算&#xff0c;不会缓存 计算属性完整写法&#xff1a; 既获取又设置 slice 截取 成绩案例 …

WebRTC Simulcast 大小流介绍与优化实践

Simulcast 是 WebRTC 中的一种标准化技术 &#xff0c;简称大小流。通过 Simulcast&#xff0c;客户端可以同时发送同一视频的多个版本。每个版本都以不同的分辨率和帧率独立编码&#xff0c;带宽较多的拉流端可以接收较高质量的视频流&#xff0c;带宽有限的拉流端则可以接收较…

kong网关使用pre-function插件,改写接口的返回数据

一、背景 kong作为api网关&#xff0c;除了反向代理后端服务外&#xff0c;还可对接口进行预处理。 比如本文提及的一个小功能&#xff0c;根据http header某个字段的值&#xff0c;等于多少的时候&#xff0c;返回一个固定的报文。 使用到的kong插件是pre-function。 除了上…

轮播图带详情插件、uniApp插件

超级好用的轮播图 介绍访问地址参数介绍使用方法&#xff08;简单使用&#xff0c;参数结构点击链接查看详情&#xff09;图片展示 介绍 带有底部物品介绍以及价格的轮播图组件&#xff0c;持续维护&#xff0c;uniApp插件&#xff0c;直接下载填充数据就可以在项目里面使用 …

Vite内网ip访问,两种配置方式和修改端口号教程

目录 问题 两种解决方式 结果 总结 preview.host preview.port 问题 使用vite运行项目的时候&#xff0c;控制台会只出现127.0.0.1&#xff08;localhost&#xff09;本地地址访问项目。不可以通过公司内网ip访问&#xff0c;其他团队成员无法访问&#xff0c;这是因为没…

老旧小区用电安全保护装置#限流式防火保护器参数介绍#

摘要 随着居民住宅区用电负荷的增加&#xff0c;用电安全问题日益突出&#xff0c;火灾隐患频繁发生。防火限流式保护器作为一种新型电气安全设备&#xff0c;能够有效预防因电气故障引发的火灾事故。本文介绍了防火限流式保护器的工作原理、技术特点及其在居民住宅区用电系统…

Ftrans数据摆渡系统 搭建安全便捷跨网文件传输通道

一、专业数据摆渡系统对企业的意义 专业的数据摆渡系统对企业具有重要意义&#xff0c;主要体现在以下几个方面‌&#xff1a; 1、‌数据安全性‌&#xff1a;数据摆渡系统通过加密传输、访问控制和审计日志等功能&#xff0c;确保数据在传输和存储过程中的安全性。 2、‌高…