【C++11】initializer_list | 右值引用 | 完美转发

一切皆可列表{ }初始化

在C++98,允许花括号{ } 对数组、结构体类型初始化。

class Data
{
public:
	Data(int y, int m, int d)
		:_y(y), _m(m), _d(d)
	{}
private:
	int _y;
	int _m;
	int _d;
};

int arr[4]={0,1,2,3};//列表初始化
Data d1={2024,03,21};//列表初始化 

C++11允许通过{ } 初始化内置类型或者用户自定义类型。同时支持省去赋值=符号

class Data
{
public:
	Data(int y, int m, int d)  :_y(y), _m(m), _d(d)
	{}
private:
	int _y;
	int _m;
	int _d;
};

int main()
{
	int a = 1;
	int b = { 1 };//支持列表初始化
	int c{ 1 };   //支持列表初始化,同时省略=

	Data d1(2024, 03, 21);
	Data d2={ 2024, 03, 21 };//支持列表初始化
	Data d3{ 2024, 03, 21 };//支持列表初始化,同时省略=

    Data* p1=new Data[]{{2023,03,21},{2023,03,22},{2023,03,23}};

	return 0;
}

创建d2时,会先调用{2024 ,03,21}列表构造出一个临时对象,再用临时对象拷贝构造d2。

如何证明?对列表的对象取别名,只有加const后才能通过。

编译器一般将 构造+拷贝构造优化成—>直接构造


std::initializer_list

在C++11中,std::initializer_list是一个模板类,它用于表示初始化列表。

当编译器遇到一个使用花括号{}的初始化列表时,它会尝试构造一个std::initializer_list对象,并将该对象传递给接受std::initializer_list参数的函数或构造函数。

initializer_list类中存在俩个指针,begin和end。

initializer_list同时支持迭代器

	initializer_list<int> il{ 1,2,3,4 };
	initializer_list<int>::iterator it = il.begin();
	while (it != il.end())
	{
		cout << *it << " ";
		++it;
	}

在C++11往后的各种容器中,都支持initializer_list构造

	vector<int> v1{ 1,2,3,4 };
	vector<string> v2{"apple","map","cat"};

 

initializer_list去构造vector的原理 

vector(initializer_list<T> it)
{
	reserve(it.size());
	for (auto& e : it)
	{
		push_back(e);
	}
}

initializer_list的数据是放在常量区,不可修改 


auto自动推演

对于较长的类型,写起来比较复杂,就要auto帮助自动推演。

map<string, string> dict = { { "sort", "排序" }, { "insert", "插入" } };
//map<string, string>::iterator it = dict.begin();
auto it = dict.begin();  //简化代码

decltype

将对象声明为指定的类型;

typeid().name返回一个字符串,字符串的内容是类型。一般用于打印。

	int x = 1;
	double y = 3.14;
	decltype(x * y) ret;
	cout << "typeid(ret).name():" << typeid(ret).name()<< endl;


新容器

array

是一个静态数组,内存是放在栈上的。

array有俩个模板参数,第一个模板参数是类型,第二个是大小

array的设计初心是为了代替数组,因为数组的越界检查不严格,因此array是严格的断言

但是在日常的使用,完全可以用vector代替。

int main()
{
	array<int, 10> a1;   //定义一个可存储10个int类型元素的array容器
	array<double, 5> a2; //定义一个可存储5个double类型元素的array容器
	return 0;
}

forward_list容器

forwar_list是一个单链表

在实际使用,forward_list的运用少于list

  • forward_list只支持头插头删,尾插尾删的复杂度是O(N);
  • forward_list支持inset_after,插入的时间复杂度是O(N);
  • 删除指定元素要找前一个结点。复杂度是O(N);

unordered_map和unordered_set容器

底层是哈希表实现的map和set


右值引用

什么是左值?

数据的表达式,通常出现在等号的左边,是可以被取地址的。

	int a = 1;
	int* b = new int;
	const int c = 2;

	int d = a; //d也是左值

什么是右值?

字母常量,表达式的返回值,函数的返回值,通常出现在等号的右边

是不能被取地址的;

	10;		//常量
	Add(10, 5);//函数返回值
	5 + 1;	//表达式返回值
  • 右值的本质是一块临时变量,没有被存储起来,即将被销毁的。因此无法取到地址。
  • 值得注意的是,函数的返回值如果返回的是一块实际存储的空间,那么就是不是右值。

 左值引用和右值引用

在C++11新特性后,增加了右值引用的玩法。

左值就是给左值取别名,右值就是给右值取别名。本文着重介绍右值。

右值引用的符号  (类型&&)

	10;		//常量
	Add(10, 5);//函数返回值
	5 + 1;	//表达式返回值

	int&& rp1 = 10;
	int&& rp2 = Add(10, 5);
	int&& rp3 = 5 + 1;

注意:
一个右值被取别名后,就被存储到特定的位置上,就能通过别名修改右值的内容;

左值引用可以引用右值吗?

不能。左值是可以被修改的,右值是常量不可被修改。将右值给左值引用,涉及权限的放大。

给左值引用加上const后,就能引用右值。

	int& rp = Add(10, 5);//出错
	const int& rp = Add(10, 5);//正常

右值引用可以引用右值吗?

右值引用只能引用右值。

右值引用可以引用move后的左值。

	10;//右值
	int i = 10;//左值
	int&& r = move(i);

主要原因:

  1. 语义不匹配:右值引用的设计初衷是为了支持移动语义,即从一个临时对象(右值)中“窃取”资源并转移到另一个对象。左值通常具有持久的存储位置,因此将其与移动语义相关联是不合适的。
  2. 避免意外行为:如果允许右值引用引用左值,那么开发者可能会不小心将左值当作右值来处理,从而导致资源被错误地移动而不是复制。这可能导致程序出现难以调试的错误。

move是一种资源转移,对于左值的资源转移,要非常的谨慎! 


右值引用的场景

移动构造函数和移动赋值运算符

就拿模拟实现的to_string函数的来说明

To_string函数体中的str是一个左值,出了函数作用域,就会被销毁。我们把这个即将销毁的值叫做 “将亡值” 。

这一条语句的执行过程是:str先构造出一个临时对象,再用临时对象拷贝构造出s对象(如果考虑优化,编译器会直接用str构造出s对象)

本文默认不考虑编译器的优化

如果str出了作用域就会被销毁,那么他的资源就被释放,反而重新拷贝出一份一模一样的资源,这是浪费效率的事情,如果在此时发生深拷贝,那么效率会更低。

		string(string&& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			cout << "string(string&& s) -- 移动构造" << endl;
			swap(s);
		}

移动构造是一种转移资源!


移动赋值

移动赋值同样也是一种转移的思想。

		string& operator=(string&& s)
		{
			cout << "string& operator=(string&& s) -- 移动赋值" << endl;
			swap(s);
			return *this;
		}

对于将亡值,编译器会优先调用移动拷贝和移动赋值。


右值通过move引用左值

通过move函数后,s1会被当成右值,调用移动构造函数,会将s1的资源转移到s2上,s1的内容会变为空。

总结:将亡值会调用移动构造和移动赋值来实例化对象

左值可以通过move转移资源,来调用移动构造和移动赋值。


万能引用

万能引用是指在模板中,即可以接收左值又可以接收右值。

template<class T>
void Fun(T&& x) //万能引用
{
	cout << x << endl;
}

int main()
{
	Fun(10);//传入右值
	int i = 1;
	Fun(i);//传左值
	return 0;
}

调用模板函数,传入左值和右值都能被Fun函数接收,故针对模板类的&&是万能引用

右值引用的属性是什么?

void Func(int& x)
{
	cout << "左值引用" << endl;
}
void Func(const int& x)
{
	cout << "const 左值引用" << endl;
}
void Func(int&& x)
{
	cout << "右值引用" << endl;
}
void Func(const int&& x)
{
	cout << "const 右值引用" << endl;
}
template<class T>
void PerfectForward(T&& t)
{
	Func(t);
}
int main()
{
	int a = 10;
	PerfectForward(a);       //左值
	PerfectForward(move(a)); //右值

	const int b = 20;
	PerfectForward(b);       //const 左值
	PerfectForward(move(b)); //const 右值

	return 0;
}

左值和右值都会被万能引用接收,而后非const调用的Func函数是左值引用。

const调用的Func函数是右值引用。

说明右值被右值引用接收后的属性是左值!

针对这一点需要注意的,假如模拟实现的vector容器push_back接收到右值,调用inset()函数(同样实现了右值版本),却发现调不动,因为右值被引用后的结果是左值。

要让insert也调用右值,就必须先move再调用insert。


完美转发

完美转发(Perfect Forwarding)是一种技术,它允许函数模板将其参数无损地转发给另一个函数,保持参数的原始值类别(lvalue或rvalue)和类型。这通常用于编写通用包装器或代理函数,例如标准库中的 std::forward 和 std::tie

template<class T>
void PerfectForward(T&& t)
{
	Func(std::forward<T>(t));
}

有了完美转发之后,就不用担心出现移动构造+赋值的情况,就会出现移动构造+移动赋值,大大减小了资源的利用。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LangChain教程 | 实践过程报错集 | 持续更新

这是本人最近在做langchain教程过程中的遇到的报错&#xff0c;不分先后顺序。 报错&#xff1a;TypeError: NoneType object is not iterable 这个报错很常见&#xff0c;咱们要看原始报错的位置是哪里&#xff0c;下面是我的截图&#xff1a; 找到源头之后&#xff0c;就在源…

使用idea运行程序,发现控制台的中文出现乱码

修改UTF-8发现没有效果&#xff0c;寻找.idea文件夹的encodings.xml文件&#xff0c;将里面的UTF-8全部变成GBK.

(一)基于IDEA的JAVA基础12

一维数组 为什么使用数组: 当我们需要存储一系列数据的时候&#xff0c;就需要用到数组&#xff0c;如果不使用数组&#xff0c;我们就要需要一个一个的去声明变量&#xff0c;这样浪费内存空间&#xff0c;同时效率低下。 什么是数组: 数组本身就是一个变量&#xff0c;只…

爱普生新一代可编程振荡器系列SG-8018

频率范围: 0.67 MHz-170 MHZ 温度范围: -40C to 105C 精度: 50 ppm including aging(包括老化) 供电电压: 1.8V, 2.5V, 3.3V(1.62V-3.63V) 低功耗: 3.2 mA-8.1 mA maximum 单端输出模式: LVCMOS 可编程上升/下降时间&#xff1a;输出使用(OE)或待机功能(ST) 4种封装尺寸…

testng接口自动化2@Test常见参数

接下来是Test注解里的一些常用参数 1,enabled 是否执行此用例&#xff0c;若enabled false&#xff0c;则不执行此方法&#xff0c;若enabled true 则此方法执行&#xff0c;如图test1的testDemo3设置为true&#xff0c;testDemo2设置为false,则testDemo2没执行 2,expecte…

蓝桥杯 历届真题 时间显示【第十二届】【省赛】【C组】

资源限制 内存限制&#xff1a;256.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s #include<bits/stdc.h> #define int long long using namespace std; const int N 1e510; int n,m,t,d; int a[2][N],b[N]; //…

Nuxt3 实战 (三):使用 release-it 自动管理版本号和生成 CHANGELOG

release-it 能做什么&#xff1f; 增加版本号并提交 Git生成变更日志&#xff08;Changelog&#xff09;并提交到 Git创建 Git 标签并推送到远程仓库发布到 npm 等软件仓库在 GitHub、GitLab 等平台创建发行版 前置知识 在看这篇文章之前&#xff0c;我们有必要了解一下 Sem…

深入了解iOS内存(WWDC 2018)笔记-内存诊断

主要记录下用于分析iOS/macOS 内存问题的笔记。 主要分析命令&#xff1a; vmmap, leaks, malloc_history 一&#xff1a;前言 有 3 种思考方式 你想看到对象的创建吗&#xff1f;你想要查看内存中引用对象或地址的内容吗&#xff1f;或者你只是想看看 一个实例有多大&#…

构建第一个ArkTS之页面和自定义组件生命周期

在开始之前&#xff0c;我们先明确自定义组件和页面的关系&#xff1a; 自定义组件&#xff1a;Component装饰的UI单元&#xff0c;可以组合多个系统组件实现UI的复用&#xff0c;可以调用组件的生命周期。页面&#xff1a;即应用的UI页面。可以由一个或者多个自定义组件组成&…

nginx到底是怎么工作的

工作流程 用户通过域名发出访问Web服务器的请求&#xff0c;该域名被DNS服务器解析为反向代理服务器的IP地址反向代理服务器接受用户的请求反向代理服务器在本地缓存中查找请求的内容&#xff0c;找到后直接把内容发送给用户如果本地缓存里没有用户所请求的信息内容&#xff0…

【Java】Java中类的初始化顺序(静态方法,静态块,非静态块,最后有流程图)

&#x1f4dd;个人主页&#xff1a;哈__ 期待您的关注 在日常使用Java的时候&#xff0c;我们都接触过new这个关键字&#xff0c;那你是否知道在我们的对象真正创建出来之前都做了哪些事情呢&#xff1f; 实际上要去判断一个类的初始化的顺序&#xff0c;需要分一下情况&…

Qt使用QWidget重绘实现圆环形渐变色进度条(支持不确定进度模式)

效果如下&#xff1a; 从纯竖直方向顶部蓝色到底部青色的渐变。 从左上角偏左45到右下角偏右45的蓝色到青色渐变。 从左上角偏左22.5到右下角偏右22.5的蓝色到青色渐变。&#xff08;这个角度渐变最好看&#xff09; 可以选择添加背景图片 支持两种模式&#xff1a;正常进度模…

媒体邀约专访如何深入的做一篇专访报道?流程分享

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 进行媒体邀约专访并深入撰写一篇专访报道是一个系统性工作&#xff0c;涉及多个环节。以下是一个详细的流程分享&#xff1a; 一、前期准备 确定专访目的与主题&#xff1a;明确专访希…

Training - 使用 WandB 配置管理模型训练过程

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://blog.csdn.net/caroline_wendy/article/details/137529140 WandB (Weights&Biases) 是轻量级的在线模型训练可视化工具&#xff0c;类似于 TensorBoard&#xff0c;可以帮助用户跟踪…

可编程网关:如何助力智慧工厂实现智能化管理

一个具体的实际案例&#xff0c;详细说明可编程网关在某汽车零部件智慧工厂中的应用细节&#xff1a; 案例背景&#xff1a; 某大型汽车零部件制造企业&#xff0c;致力于提升生产效率、降低运营成本、确保产品质量&#xff0c;决定对其传统工厂进行全面数字化改造&#xff0…

从零开始:一步步学习爬虫技术的实用指南(一)

从零开始&#xff1a;一步步学习爬虫技术的实用指南&#xff08;一&#xff09; Urllib1.什么是互联网爬虫2.爬虫核心3.爬虫的用途4.爬虫的分类4.1 通用爬虫&#xff1a;4.1 聚焦爬虫&#xff1a; 5.反爬手段5.1 User‐Agent&#xff1a;5.2.代理IP5.3.验证码访问5.4.动态加载网…

深入理解JVM后端优化技术-逃逸分析(Escape Analysis)

相关系统 深入理解jvm执行引擎-CSDN博客 深入理解JVM后端优化技术-方法内联-CSDN博客 定义 当一个对象在方法里面被定义后,它可能让外部方法所引用,作为调用参数传递到其它的方法中,这种称为方法逃逸;还有可能被外部线程访问到,赋值给可以在其它线程中访问的实例数量,这…

【测试篇】Selenium + Java环境搭建

文章目录 Selenium Java环境搭建配置系统环境变量PATH验证环境是否搭建成功常见问题&解决办法 Selenium Java环境搭建 Java版本最低要求为8&#xff0c;这里默认大家都下载好了Java。&#x1f606; 下载chrome浏览器&#xff08;点我下载&#xff09; 观察chrome版本。…

混合专家(MoE)模型

文心一言 混合专家模型&#xff08;Mixture of Experts&#xff0c;简称MoE&#xff09;是一种基于Transformer架构的模型设计策略。它通过将多个模型&#xff08;称为“专家”&#xff09;直接结合在一起&#xff0c;以获得更好的预测性能。这种模型特别适用于处理大规模数据…

学习操作系统之多道批处理系统

1964年IBM生产了第一台小规模集成电路计算机IBM System/360&#xff08;第三代计算机&#xff09;&#xff0c;并为该计算机开发了OS/360操作系统&#xff0c;是第一个多道批处理系统。 多道批处理的运行机制&#xff1a; 多道批处理系统同样要求事先将多道作业存放到外存上并…