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

news2024/11/25 6:39:50

一切皆可列表{ }初始化

在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));
}

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

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

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

相关文章

基于ADS的PDK---DemoKit的切比雪夫滤波器RF芯片设计

基于ADS的PDK—DemoKit的切比雪夫滤波器RF芯片设计 由于版权原因&#xff0c;很少有完整的ADS的PDK在网上流传的&#xff0c;网上CSDN里面一些台积电的PDK都是只能老版本ADS2008才能用&#xff0c;或者干脆是Cadence导出来的&#xff08;Cadence导出PDK到ADS参考教程&#xff…

PPT 操作

WPS 版式 PPT中&#xff0c;巧妙使用母版&#xff0c;可以提高效率。 双击母版&#xff0c;选择其中一个版式&#xff0c;插入装饰符号。 然后选择关闭。 这个时候&#xff0c;在该版式下的所有页面&#xff0c;就会出现新加入的符号。不在该版式下的页面&#xff0c;不会出现…

飞桨Ai(一)基于训练后的模型进行信息提取

基准 本博客基于如下视频&#xff1a; 发票抬头信息抽取之环境搭建 - 基于飞浆开源项目发票抬头信息抽取之数据标准模型训练 - 基于飞浆开源项目 步骤 1、准备工作 下载python&#xff1a;【Python】Windows&#xff1a;Python 3.9.2 下载和安装&#xff08;建议3.9&#…

QDateTimeEdit设置按钮宽度无效

在对QDateTimeEdit组件的小按钮用qss样式加图标的时候&#xff0c;发现设置的宽度无效&#xff0c;原因是spacing属性必须设置才行。

【MATLAB源码-第29期】基于matlab的MIMO,MISO,SIMO,SISO瑞利rayleigh信道容量对比。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 1. SISO&#xff08;单输入单输出&#xff09;&#xff1a; - SISO 是指在通信系统中&#xff0c;只有一个天线用于传输信号&#xff0c;也只有一个天线用于接收信号的情况。这是最简单的通信方式。 2. SIMO&#xff08;单…

回归预测 | MATLAB实现BO-GRNN贝叶斯优化广义回归神经网络多输入单输出预测

回归预测 | MATLAB实现BO-GRNN贝叶斯优化广义回归神经网络多输入单输出预测 目录 回归预测 | MATLAB实现BO-GRNN贝叶斯优化广义回归神经网络多输入单输出预测预测效果基本介绍程序设计参考资料预测效果 基本介绍

C++奇迹之旅:探索类对象模型内存的存储猜想

文章目录 &#x1f4dd;前言&#x1f320; 类的实例化&#x1f309;类对象模型 &#x1f320; 如何计算类对象的大小&#x1f309;类对象的存储方式猜想&#x1f320;猜想一&#xff1a;对象中包含类的各个成员&#x1f309;猜想二&#xff1a;代码只保存一份&#xff0c;在对象…

韩顺平Java | C24 MySQL数据库(下)

※多表查询 笛卡尔集&#xff1a;查询两个表&#xff0c;默认无条件情况下&#xff0c;取出第一张表中的每一条记录和第二张表的每一条记录进行组合&#xff0c;返回row1*row2条记录数&#xff0c;包含两张表的所有列 内连接 # 写出正确的过滤条件&#xff1a;多表查询条件不…

【linux】yum 和 vim

yum 和 vim 1. Linux 软件包管理器 yum1.1 什么是软件包1.2 查看软件包1.3 如何安装软件1.4 如何卸载软件1.5 关于 rzsz 2. Linux编辑器-vim使用2.1 vim的基本概念2.2 vim的基本操作2.3 vim命令模式命令集2.4 vim底行模式命令集2.5 vim操作总结补充&#xff1a;vim下批量化注释…

9. 软件登陆界面-2

窗口组件 1.组件的属性 组件的位置 组件的可视 2.组件的事件 窗口_创建完毕 窗口_托盘事件&#xff1b;带有参数的事件的使用方法。 3.组件的方法 置托盘图标 销毁&#xff08;&#xff09; 编辑框组件 1.编辑框的属性 内容 是否允许多行 输入方式 密码遮盖字符…

单链表专题

文章目录 目录1. 链表的概念及结构2. 实现单链表2.1 链表的打印2.2 链表的尾插2.3 链表的头插2.4 链表的尾删2.5 链表的头删2.6 查找2.7 在指定位置之前插入数据2.8 在指定位置之后插入数据2.9 删除pos节点2.10 删除pos之后的节点2.11 销毁链表 3. 链表的分类 目录 链表的概念…

【Linux】初识Linux操作系统

目录 一、shell 二、Linux命令的分类 三、Linux命令的格式 四、编辑Linux命令行的辅助操作 五、查看命令使用说明的方法 六、基础命令 一、shell ●Linux系统中运行的一个特殊程序&#xff0c;位于用户与内核之间 ●作用&#xff1a;作为“翻译官”&#xff0c;接收用户…

基于Java+SpringBoot+Vue网络相册设计与实现(源码+文档+部署+讲解)

一.系统概述 网络相册设计与实现的目的是让使用者可以更方便的将人、设备和场景更立体的连接在一起。能让用户以更科幻的方式使用产品&#xff0c;体验高科技时代带给人们的方便&#xff0c;同时也能让用户体会到与以往常规产品不同的体验风格。 与安卓&#xff0c;iOS相比较起…

C++ 类和对象(中篇)

类的6个默认成员函数 如果一个类中什么成员都没有&#xff0c;简称为空类。空类中什么都没有吗&#xff1f;并不是的&#xff0c;任何一个类在我们不写的情 况下&#xff0c;都会自动生成下面6个默认成员函数。 构造函数&#xff1a; 定义&#xff1a;构造函数是一个特殊的成员…

【PHP系统学习】——Laravel框架数据库的连接以及数据库的增删改查的详细教程

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

【招贤纳士】长期有效

【招贤纳士】长期有效&#xff0c;有意者联系 一、SLAM算法工程师工作内容&#xff1a;任职资格&#xff1a; 二、规划算法工程师工作内容&#xff1a;任职资格&#xff1a; 工作地点&#xff1a;深圳南山 公司行业&#xff1a;家用扫地机器人 待遇从优&#xff0c;有机器人比赛…

CAD导入GIS平台常见问题大全

1.CAD导入图新地球报【坐标超出范围】、【导入失败】 一般是投影不对&#xff0c;多数是中央经线选错了&#xff0c;或者是没注意是否有带号 这种情况&#xff0c;先打开CAD软件&#xff0c;通过id命令看一下数据的坐标&#xff0c;如下图 看到坐标是这样式的&#xff0c;X达…

达梦数据库审计相关参数

达梦数据库审计相关参数 基础环境 操作系统&#xff1a;Red Hat Enterprise Linux Server release 7.9 (Maipo) 数据库版本&#xff1a;DM Database Server 64 V8 架构&#xff1a;单实例1 查看审计相关的参数 查看AUD相关的参数。 1.1 查看dm.ini配置文件。 在dm.ini配置文…

gurobi不同版本切换

每年年底&#xff0c;gurobi都会推出新版本。新版本是大的迭代更新&#xff0c;求解问题的效率和精度都会提升。官方人员一般会建议我们安装最新的版本&#xff0c;此外&#xff0c;写论文审稿专家也会建议我们使用较新的版本。 从我们现装的版本切换到新版本。我以往的做法是…

【CVE-2023-38831】进行钓鱼攻击的研究

本文仅仅是对相关漏洞利用的学习记录&#xff0c;请各位合法合规食用&#xff01; WinRAR是一款文件压缩器,该产品支持RAR、ZIP等格式文件的压缩和解压等。WinRAR在处理压缩包内同名的文件与文件夹时代码执行漏洞,攻击者构建由恶意文件与非恶意文件构成的特制压缩包文件,诱导受…