C++:C++11新特性---右值引用

文章目录

  • 初始化方式
  • 显示查看类型
  • initializer_list
  • decltype
  • 左值引用和右值引用
    • move
    • 左右值引用的场景
  • 万能引用和完美转发

本篇总结C++11新特性

初始化方式

C++11对参数列表的初始化有了更明确的定义,可以这样进行定义

// 列表初始化
void test1()
{
	// 旧版本
	int x = 0;
	// 新版本
	int y{ 0 };
	int z = { 0 };
	int arr[]{ 10,20,30 };
	int* pa = new int[5]{ 1,2,3,4,5 };
}

在对类的赋值的时候,也可以这样进行赋值

struct t
{
	int a;
	int b;
};

void test2()
{
	t* pt = new t[5]{ 1,2 };
}

显示查看类型

// 查看类型
void test3()
{
	int a = 1;
	cout << typeid(a).name() << endl;
	auto it = map<int, int>().begin();
	cout << typeid(it).name() << endl;
}

运行结果:

int
class std::_Tree_iterator<class std::_Tree_val<struct std::_Tree_simple_types<struct std::pair<int const ,int> > > >

initializer_list

这是什么呢?如何理解这个类型?先看一下在什么场景中会出现这个东西

void test4()
{
	auto lit = { 1,2,3,4 };
	cout << typeid(lit).name() << endl;
}

那么这个东西是干什么的呢?有什么用呢?

在这里插入图片描述
在C++11中,对于STL的各类容器的构造函数中,新增了这样的构造方式,有点类似于一个数组,它里面可以存储任意类型的数据,然后可以交给vector来实现构造,因此下面就要对{}进行一个对比

void test5()
{
	// 利用initializer_list进行初始化
	vector<int> v{ 1,2,3,4,5 };
	auto lit = { 1,2,3,4 };
	vector<int> vc(lit);

	// 调用参数初始化列表进行初始化
	int arr[]{ 1,2,3,4,5 };
}

这两种写法看似,但是实际上底层是完全不同的两种实现的方式

decltype

// decltype的一些使用使用场景
template<class T1, class T2>
void F(T1 t1, T2 t2)
{
	decltype(t1 * t2) ret;
	cout << typeid(ret).name() << endl;
}

void test6()
{
	const int x = 1;
	double y = 2.2;
	decltype(x * y) ret;
	decltype(&x) p;
	cout << typeid(ret).name() << endl;
	cout << typeid(p).name() << endl;
	F(1, 'a');
}

简单来说,就是可以把你要新定义的一个类型进行人为的定义,定义成一个你想让它变成的类型,使用场景也不算多,但是可以这样进行使用

左值引用和右值引用

首先要解决一个问题,什么是左值?

之前可能会说,左值就是等号左边的值,这肯定是不对的,左值是一个表示数据的表达式,可以获取它的地址,也可以进行赋值,通常来说它出现在赋值符号的左边,右值不能出现在赋值符号的左边,定义的时候,const修饰符修饰的变量不可以对它进行赋值,但是可以对它取地址,因此,可以说左值引用就是给左值的引用,给左值取别名

常见的左值

int* p = new int(0);
int b = 1;
const int c = 2;

因此我们说,可以对这些起一个别名

int*& rp = p;
int& rb = b;
const int& rc = c;
int& pvalue = *p;

常见的右值

10;
x + y;
fmin(x, y);

而对这些起别名就是右值引用

int&& rr1 = 10;
double&& rr2 = x + y;
double&& rr3 = fmin(x, y);

对于右值引用来说,可以理解为,右值本身是不能取地址的,但是给右值取别名之后,右值就会被存储到某些地方,此时就可以取到它的地址

左值引用只能引用左值,不能引用右值,如果想引用右值需要用const

int main()
{
  // 左值引用只能引用左值,不能引用右值。
  int a = 10;
  int& ra1 = a;  // ra为a的别名
  //int& ra2 = 10;  // 编译失败,因为10是右值
  // const左值引用既可引用左值,也可引用右值。
  const int& ra3 = 10;
  const int& ra4 = a;
  return 0;
}

move

右值只能引用右值,不能引用左值,但是右值可以引用move之后的左值

void test7()
{
	// 右值引用只能右值,不能引用左值。
	int&& r1 = 10;
	// error C2440: “初始化”: 无法从“int”转换为“int &&”
	// message : 无法将左值绑定到右值引用
	int a = 10;
	//int&& r2 = a;
	// 右值引用可以引用move以后的左值
	int&& r3 = std::move(a);
}

左右值引用的场景

那左右值引用能干啥呢?

左值引用

对比下面两种传参方式

void func(const string& str);
void func(string str);

从传参的效率上就不一样了,对于传引用来说,每次传参的代价是很低的,只需要把变量的地址给过去就可以了,但是对于传值来说就不一样了,传参的代价是相当高的,需要把原始的参数拷贝给新的值

也就是说,左值引用做参数减少了拷贝,提高效率的使用场景和价值

左值引用的缺陷

那左值引用有什么缺陷呢?

第一个是,当函数返回的对象是一个局部对象的时候,是不可以使用传引用返回的,因为这个变量出了作用域就被销毁了,因此不能使用传左值引用,只能进行传值返回,例如下面的场景

	mystring::string to_string(int x)
	{
		mystring::string ret;
		while (x)
		{
			int val = x % 10;
			x /= 10;
			ret += ('0' + val);
		}
		reverse(ret.begin(), ret.end());
		return ret;
	}

此时的string是一个局部变量,如果使用传引用返回会访问一个不存在的地址,这是不被允许的,但如果采用传值返回,又会导致增加了拷贝构造的次数,并且可能还是两次

在新的编译器中,函数得到的这个string在返回的时候,会直接赋给新的值,调用一次拷贝构造,而在旧一点的编译器中,从函数的返回值会先拷贝构造一次到一个临时常量,再从这个临时常量拷贝构造一次到外部定义的string中

而使用移动构造,可以解决这个问题,编译器会默认使用最匹配的参数进行调用,因此会优先使用移动构造

搭配move函数

move函数的作用,就是单纯的把左值转换成右值引用,由此来实现移动语义

那这样有什么用呢?该如何理解这个意思呢?

如果有下面的代码:

string s2(s1)

这样的语句代表的含义是,调用拷贝构造,这里的s1是一个左值

但是如果要是改成这样

string s2(move(s1))

这样就不一样了,把s1放move函数中,这样就可以把s1当成一个右值,而右值是可以调用移动构造的,这样就可以不用调用拷贝构造浪费空间,而是可以直接的把值置换到我们所需要的s2里面,但是这样其实是不好的,这样会导致,虽然确实把s2的值填充了,但是却把s1的值架空了

简单来说,移动构造就是把资源全部偷过来,把原来的资源都架空

再举一个例子:

int main()
{
list<string> lt;
string s1("1111");
// 这里调用的是拷贝构造
lt.push_back(s1);
// 下面调用都是移动构造
lt.push_back("2222");
lt.push_back(std::move(s1));
return 0;
}

如果只是简单的调用s1,那么s1会被当成是一个左值,而左值会调用的是拷贝构造,但是如果把它强制转换成右值,那么就会调用的是移动构造,很明显,移动构造的使用成本是要比拷贝构造低很多的

万能引用和完美转发

前面关于右值引用中和前面有比较大不同的一点就是出现了&&符号,如果把这个符号看成是右值引用的标识符,也是不对的,C++11在模板中也新增了关于&&符号,这个符号代表的是万能引用,而不是右值引用,简单来说就是,既能接收左值也能接收右值

模板的万能引用只是提供了一个可以接收左值和右值的能力,一般来说是不可以两个都接收的
实际的使用中,引用类型的作用会限制接收的类型,会变成左值,而如果想要在传递的过程中保持右值的属性,就需要用到万能引用和完美转发

下面做一个实验来验证功能

void check(int& t)
{
	cout << "左值引用" << endl;
}

void check(int&& t)
{
	cout << "右值引用" << endl;
}

void check(const int& t)
{
	cout << "const左值引用" << endl;
}

void check(const int&& t)
{
	cout << "const右值引用" << endl;
}

void test8()
{
	const int a = 1;
	const int& b = 1;
	int c = 0;
	check(a);
	check(b);
	check(c);
	check(1);
	check(move(a));
	check(move(b));
}

在这里插入图片描述

结果也是符合预期的,把上述代码进行适当更改

void check(int& t)
{
	cout << "左值引用" << endl;
}

void check(int&& t)
{
	cout << "右值引用" << endl;
}

void check(const int& t)
{
	cout << "const左值引用" << endl;
}

void check(const int&& t)
{
	cout << "const右值引用" << endl;
}

void func1(int t)
{
	check(t);
}

void test8()
{
	const int a = 1;
	const int& b = 1;
	int c = 0;
	func1(move(a));
}

此时运行结果是左值引用,这是为什么?其原因是进入func1函数后,函数参数就从右值变成左值了,此时它的右值属性就会消失,因此出现了下面的用法:

template<class T>
void func1(T&& t)
{
	check(t);
}

此时运行结果是const左值引用,这说明它保持了const属性,但是依旧没有保持右值的属性,右值的属性依旧被退化成左值了

再进行改良

template<class T>
void func1(T&& t)
{
	check(forward<T>(t));
}

此时运行结果就是const右值引用了,而新增的这个forward其实就是完美转发,它可以保障原来的属性,把原来这个值的属性转发出去,严格意义来说保持的是左值和右值,而如果没有万能引用运行结果依旧会丢失const属性

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

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

相关文章

VBA高级应用30例:Ribbon(功能区)的介绍

《VBA高级应用30例》&#xff08;版权10178985&#xff09;&#xff0c;是我推出的第十套教程&#xff0c;教程是专门针对高级学员在学习VBA过程中提高路途上的案例展开&#xff0c;这套教程案例与理论结合&#xff0c;紧贴“实战”&#xff0c;并做“战术总结”&#xff0c;以…

基于SSM的零食物语购物商城的设计与实现

基于SSM的零食物语购物商城的设计与实现 摘要&#xff1a;“日常网购”作为一种比传统购物更便捷的方式&#xff0c;越来越受到大众的欢迎和认可。因此&#xff0c;系统的设计和应用技术对零食购物商城网站的要求越来越高&#xff0c;于是本系统应运而生。基于SSM[1]的零食购物…

linux CentOS MobaXterm 通过X11 Forwarding 在本地开启图形可视化窗口

第一步 操作系统安装图形界面 X11 Forwarding dnf install xorg-x11-xauth xorg-x11-fonts-* xorg-x11-font-utils xorg-x11-fonts-Type1 xclock第二步 修改参数&#xff0c;启用X11 Forwarding vim /etc/ssh/sshd_config修改参数X11Forwarding yes和X11UseLocalhost no #Al…

苹果mac屏幕投屏镜像工具AirServer2024

airserver 是什么软件&#xff1f;AirServer 是一款 Airplay Mac屏幕镜像应用&#xff0c;AirServer可以通过 mac 实时接收iPhone、iPad以及Android设备的实时屏幕画面。AirServer 可以将一个简单的大屏幕或投影仪变成一个通用的屏幕镜像接收器。在您的大屏幕上启用 AirServer …

Vue快速实践总结 · 下篇

文章目录 组件间通信方式父 --> 子通信props插槽 子 --> 父通信&#xff08;自定义事件&#xff09;任意组件通信全局事件总线消息订阅与发布 Vuex工作原理运行环境简单使用GettersmapState与mapGettersmapActions与mapMutations模块化 命名空间 VueRouter路由的作用与分…

什么是软件成分分析(SCA)?

来源&#xff1a;艾特保IT 虹科干货 | 什么是软件成分分析&#xff08;SCA&#xff09;&#xff1f; 欢迎关注虹科&#xff0c;为您提供最新资讯&#xff01; 大家或许都发现了&#xff0c;开发人员愈发依赖开源代码来快速为其专有软件添加功能。据估计&#xff0c;开源代码占…

NX二次开发UF_MTX3_x_vec 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_MTX3_x_vec Defined in: uf_mtx.h void UF_MTX3_x_vec(const double mtx [ 9 ] , double x_vec [ 3 ] ) overview 概述 Returns the X-direction vector of a matrix. 返回矩阵…

flutter 文本不随系统设置而改变大小[最全的整理]

文本不随系统设置而改变大小[三] 前言方案十三&#xff1a;使用Flexible方案十四&#xff1a;使用MediaQueryData的textScaleFactor属性方案十五&#xff1a;使用FractionallySizedBox方案十六&#xff1a;使用自定义文本样式方案十七&#xff1a;使用自定义绘制&#xff08;Cu…

直播场景视频和特效解决方案

直播已经成为企业与消费者互动的重要方式&#xff0c;如何提供优质的直播内容&#xff0c;提升直播效果&#xff0c;以及实现直播内容的商业化转化&#xff0c;一直是企业面临的重要挑战。为此&#xff0c;美摄科技提供了一套全面的直播场景解决方案&#xff0c;帮助企业解决这…

业务流程图用什么软件绘制?

在企业的日常工作中&#xff0c;对于业务流程的把控和优化显得非常重要。为了更好地理解和管理业务流程&#xff0c;业务流程图便应运而生。 业务流程图是企业管理的图形化工具&#xff0c;它描述了企业在生产和服务提供过程中&#xff0c;在各个环节中所涉及的各种操作、任务…

NX二次开发UF_MTX3_rotate_about_axis 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan UF_MTX3_rotate_about_axis Defined in: uf_mtx.h void UF_MTX3_rotate_about_axis(const double rotation_axis [ 3 ] , double rotation_angle, double mtx [ 9 ] ) overview 概述…

Kanna库代码示例

编写一个使用Kanna库的网络爬虫程序。以下是代码的详细解释&#xff1a; swift import Kanna // 创建一个对象 let proxy Proxy(host: ") // 创建一个Kanna对象 let kanna Kanna(proxy: proxy) // 创建一个请求对象 let request Request(url: "") // 使用…

更快更稳的剃须伙伴,DOCO黑刃电动剃须刀体验

现在的电动剃须刀选择很多&#xff0c;我觉得三刀头的旋转式剃须刀很好用&#xff0c;比如我目前用的这款DOCO黑刃剃须刀&#xff0c;在剃须效率、舒适度和易用性等方面都表现不错&#xff0c;很适合日常使用。 这款剃须刀采用磁吸式刀头&#xff0c;这一设计非常方便清洁与更换…

软件测试面试时问你的项目经验,你知道该怎么说吗?

很简单&#xff0c;我来给你们一个公式 0 自我介绍&#xff0c;名字 学历 荣誉。 1 简述项目背景&#xff0c;你身处这个项目是做什么的。 不要太细&#xff0c;试着引导一下面试官让他提问。这样&#xff0c;请问您对此有什么疑问吗&#xff1f; 2 简述 你在项目中的角色&…

vue3中的Fragment、Teleport、Suspense新组件

Fragment组件 在Vue2中: 组件必须有一个根标签 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中 好处: 减少标签层级, 减小内存占用 <template><div style"font-size: 14px;"><p> 组件可以没有根标签</p&g…

C语言——输入 10 个数,分别统计其中正数、负数、零的个数

#include <stdio.h> int main() {int numbers[10]; // 存储输入的10个数int positive_count 0; // 正数计数器int negative_count 0; // 负数计数器int zero_count 0; // 零计数器// 输入10个数printf("请输入10个数&#xff1a;\n");for (int i 0; i …

【计算机网络笔记】ARP协议

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…

神经网络可视化——基于torchviz绘制模型的计算图

神经网络可视化——基于torchviz绘制模型的计算图 第一步、安装 graphviz 和 torchviz 库 第二步、编写代码生成计算图 第三步、安装graphviz软件 在深入理解深度学习模型时&#xff0c;可视化网络结构是一个非常有用的手段。今天介绍如何使用 torchviz 和 graphviz 来生成网…

淘宝平台商品详情平台订单接入说明

一 文档说明 本文档面向对象为电商平台商品详情数据和订单进行管理的第三方开发者或自研商家 二 支持范围 目前API已经支持订单的接单、取消、退单处理等操作。如果您的订单管理需求现有API不能满足&#xff0c;可以联系我们提出API需求。 参数说明 通用参数说明 参数不要乱…

Ubuntu 20.0 + mysql 8.0 用户和密码修改

第一步 下载&#xff08;简单,注意联网&#xff09;Ubuntu 终端输入以下两行命令 (1) 数据库的服务端及客户端数据库的开发软件包 sudo apt-get install mysql-server mysql-client (2) 数据库的开发软件包 sudo apt-get install libmysqlclient-dev 第二步 查看是否安装成功 …