【C++】C++11之右值引用

news2024/9/21 12:43:59

目录

一、前言

二、概念

2.1 左值和左值引用

2.2 右值和右值引用

三、左值引用与右值引用的区别

四、应用场景

4.1 左值引用的短板(将亡值)

4.2 右值引用的意义(移动语义)

五、右值引用引用左值

六、万能引用和完美转发


一、前言

在传统的C++语法中就有引用,而C++11中又新增了右值引用的语法特性,从此传统的引用就变为了左值引用。无论是哪种引用,都是给一个对象取别名,不过二者针对的对象种类不同

右值引用是一块晦涩难懂的语法糖,何为左值?何为右值?何为左值引用?何为右值引用?


二、概念

2.1 左值和左值引用

左值(lvalue)是一个表达式,它表示一个可被标识的(变量或对象的)内存位置,并且允许使用&操作符来获取这块内存的地址。如果一个表达式不是左值,那它就被定义为右值。

简单来说,左值就是可以出现在赋值符号左侧的值,一个表示数据的表达式(如变量名或指针)就是左值;可以被取地址的就是左值;可以被赋值的就是左值。

而左值引用很好理解,就是对左值的引用,符号为& 

例如:

int main()
{
	int a = 1; //a是左值
	int* p = new int(1); //*p和p是左值
	const int b = 1; //b是左值

	int& ra = a; //左值引用
	int*& rp = p; //左值引用
	const int& rb = b; //左值引用
	return 0;
}

2.2 右值和右值引用

一个表达式如果不是左值,那么就是右值。如字面常量、表达式返回值、非左值引用返回的函数返回值等都是右值。

右值可以出现在赋值符号的右边,但是一定不能出现在赋值符号的左边。右值不能被取地址

右值引用就是对右值的引用,符号为&&

例如:

int func(int x, int y)
{
	return min(x, y);
}

int main()
{
	//右值与右值引用
	int&& r1 = 1;
	double&& r2 = 1.1 + 2.2;
	int&& r3 = func(1, 2);

	//右值不能出现在赋值符号左侧
	10 = 1; //报错
	func(1, 2) = 3; //报错
	return 0;
}

需要注意,右值虽然不能被取地址,但是右值被引用后会存储到特定位置,可以取到该位置的地址并用这个地址对其进行修改。

虽然看上去很矛盾,但是这是有必要的,因为后面提到的移动构造和移动赋值中就需要对右值进行修改。由此我们也可以得出结论:右值被右值引用引用以后的属性是左值

如果不想被修改,可以用const右值引用。

int main()
{
	//右值与右值引用
	int&& r1 = 1;
	const double&& r2 = 1.1 + 2.2;

	r1 = 10;
	r2 = 3.3; //报错
	return 0;
}


三、左值引用与右值引用的区别

左值引用只能引用左值,不能引用右值;但const左值引用既可以引用左值也可以引用右值

int main()
{
	//左值引用
	int& r1 = 1; //报错

	//const左值引用
	const int& r3 = 1;
	return 0;
}

像1这种常量是具有常性的,无法被左值引用,否则会造成权限放大,加上const就不会权限放大了

同样,右值引用只能引用右值,不能引用左值;但右值引用可以引用move后的左值

何为move后的左值?我们可以对一个左值进行move操作将其转换为右值,例如:

int main()
{
	int a = 10; //左值
	//右值引用
	int&& r1 = a; //报错

	//右值引用引用move后的左值
	int&& r2 = move(a);
	return 0;
}

关于move函数在后面会进行详细讲解


四、应用场景

4.1 左值引用的短板(将亡值)

既然const左值引用既可以引用左值又可以引用右值,那我们为什么还需要右值引用呢?

实际上左值引用在某些情况下依然存在短板,需要右值引用才能解决

我们都知道,在函数的参数和返回值中使用引用可以提高效率。但是当函数的返回对象是一个局部变量,出了函数作用域就被销毁了,那么我们就不能使用左值引用返回了。例如:

像这样,ret是函数内的局部变量,出了函数作用域就被销毁,无法使用引用返回,只能使用传值返回进行拷贝构造

对于这种完成资源拷贝或转移后就被销毁的值,我们叫做将亡值。将亡值也是右值

4.2 右值引用的意义(移动语义)

对于这类将亡值,我们想要的是它的资源,但是如果还要为此进行额外的深拷贝的话就不够优雅了

因此C++11引入了移动语义,增加了移动构造移动拷贝,本质就是直接将右值的资源占为己有,而不再需要新开空间拷贝数据,提高了效率,例如:

像上面的情况,编译器就会优先使用移动构造和移动拷贝,把将亡值的资源直接转移 

移动构造和移动拷贝对于需要深拷贝的自定义类型有意义,对于只需要浅拷贝的类型来说意义不大

我们也可以自己模拟实现最基本的移动构造和移动拷贝,看看编译器是否真的会调用

//string.h
namespace Eristic
{
	class string
	{
		string(const char* str = "")
			:_size(strlen(str))
		{
			_capacity = _size == 0 ? 3 : _size;
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}

		string(const string& s)
			:_size(s.size())
		{
			_capacity = _size;
			_str = new char[_capacity + 1];
			strcpy(_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;
		}

		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_capacity, s._capacity);
			std::swap(_size, s._size);
		}

        ~string()
		{
			delete[] _str;
			_capacity = _size = 0;
		}

        //...

		char* _str;
		size_t _size;
		size_t _capacity;
	};
}

//test.cpp
#include <iostream>
#include <assert.h>
using namespace std;
#include "string.h"

Eristic::string func()
{
	Eristic::string ret = "hello";
	return ret;
}

int main()
{
	Eristic::string s;
	s = func();
	return 0;
}

可以看到,编译器确实会优先调用移动构造和移动赋值

不只是构造和拷贝构造,像vector的insert也有左值引用和右值引用版本


五、右值引用引用左值

前面提到,右值引用可以引用move后的左值,具体是如何实现的呢?

可以看到,a被move后还是左值,这是怎么回事?

实际上,move函数并不会把左值修改为右值,而是其返回值是一个右值

move函数的实现如下:

template<class _Ty>
inline typename remove_reference<_Ty>::type&& move(_Ty&& _Arg) _NOEXCEPT
{
    // forward _Arg as movable
    return ((typename remove_reference<_Ty>::type&&)_Arg);
}

需要注意,如果我们将一个左值经过move处理后调用移动构造去构造另一个对象,原来的左值的资源就被转移给该对象了,这个左值就被置空了


六、万能引用和完美转发

模板中的&&不代表右值引用,而是万能引用,其既可以接收右值也可以接收左值,例如:

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<typename T>
void PerfectForward(T&& t)
{
	Func(t);
}

int main()
{
	PerfectForward(10); //右值
	int a;
	PerfectForward(a); //左值
	PerfectForward(move(a)); //右值
	const int b = 8;
	PerfectForward(b); //const左值
	PerfectForward(move(b)); //const右值
	return 0;
}

但是,模板中的万能引用只是提供了能够同时接收左值引用和右值引用的能力。前面提到过,右值引用的属性是左值,在后续使用中它们都会退化为左值引用,所以上面代码的结果如下:

全都调用了左值引用版本

如果我们希望在传递过程中保留右值的属性怎么办呢?就需要使用接下来提到的完美转发

forward是一个标准库函数,它能够根据传递给它的参数的类型,保持该参数的值类别(左值或右值)

如果一个参数是右值,经过forward处理后,它还是右值;如果是左值,处理后仍然是左值

完.

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

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

相关文章

从零开始手写STL库:HashTable

从零开始手写STL库–HashTable的实现 Gihub链接&#xff1a;miniSTL 文章目录 从零开始手写STL库–HashTable的实现HashTable是什么HashTable需要包含什么函数基础成员部分基础函数部分可用函数部分 其他函数总结 HashTable是什么 HashTable在STL中直接出现的情况并不多&…

宝塔部署springboot vue ruoyi前后端分离项目,分离lib、resources

1、“文件”中创建好相关项目目录,并将项目相关文件传到对应目录 例如&#xff1a;项目名称/ #项目总目录 api/ #存放jar项目的Java项目文件 manage/ #vue管理后端界面 …

计网面试题

OSI七层模型 物理层&#xff0c;数据链路层&#xff0c;网络层&#xff0c;传输层&#xff0c;会话层&#xff0c;表示层&#xff0c;应用层 TCP/IP四层模型 应用层&#xff0c;传输层&#xff0c;网络层&#xff0c;网络层接口 HTTP&#xff08;超文本传输协议&#xff09;…

C语言典型例题20

《C程序设计教程&#xff08;第四版&#xff09;——谭浩强》 例题2.8 有两个整形变量a和b&#xff0c;将他们两个的值进行互换 代码&#xff1a; //《C程序设计教程&#xff08;第四版&#xff09;——谭浩强》 //例题2.8 有两个整形变量a和b&#xff0c;将他们两个的值进行…

【通俗理解】牛顿第三定律与生命反应——超越物自在主义的探讨

【通俗理解】牛顿第三定律与生命反应——超越物自在主义的探讨 牛顿第三定律与生命的类比 你可以把牛顿第三定律比作一个“即时回应器”&#xff0c;它表明作用力和反作用力总是同时、同量且反向发生。而生命则像是一个“策略玩家”&#xff0c;它不仅回应&#xff0c;还能操纵…

flutter存储日志文件代码

如题所示&#xff0c;我需要将flutter运行得到的相关日志信息存储成文件&#xff0c;可以按时间或者文件大小&#xff0c;将日志文件拆分存储成多个日志文件。在项目中我已实现这个功能&#xff0c;因为觉得有用&#xff0c;所以单独简化出来放在一个demo中。 这里给出我整理出…

掌握分布式系统的38个核心概念

天天说分布式分布式&#xff0c;那么我们是否知道什么是分布式&#xff0c;分布式会遇到什么问题&#xff0c;有哪些理论支撑&#xff0c;有哪些经典的应对方案&#xff0c;业界是如何设计并保证分布式系统的高可用呢&#xff1f; 1. 架构设计 这一节将从一些经典的开源系统架…

day11 Stream流、File、方法递归、字符集、IO流(巨多API)

目录 1.Stream流 1.1认识Stream流 1.2Stream流处理数据的步骤 1.3获取Stream流 1.3.1获取集合的Stream流 1.3.2获取数组的Stream流 1.4Stream提供的常用方法 1.5终结、收集Stream流 1.5.1终结方法 1.5.2收集方法 2.可变参数 2.1可变参数是什么 2.2可变参数的特点和…

80.WEB渗透测试-信息收集-框架组件识别利用(4)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;79.WEB渗透测试-信息收集-框架组件识别利用&#xff08;3&#xff09;-CSDN博客 springb…

基于java的人居环境整治管理系统(源码+lw+部署文档+讲解等)

前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝20W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f447;&#x1f3fb;…

《如鸢》开通官号,女性向游戏爆款预定

今天&#xff0c;备受瞩目的沉浸式剧情卡牌手游《如鸢》正式开通了官方社媒账号并发布了玩家信。 《如鸢》由灵犀互娱倾力打造&#xff0c;游戏不仅拥有跌宕起伏的权谋剧情&#xff0c;更采用Live2D技术&#xff0c;为玩家带来沉浸式的游戏体验&#xff0c;吸引了众多玩家关注。…

基于SpringBoot+Vue的企业客户信息反馈平台(带1w+文档)

基于SpringBootVue的企业客户信息反馈平台(带1w文档) 基于SpringBootVue的企业客户信息反馈平台(带1w文档) 企业客户信息反馈平台的开发运用java技术&#xff0c;MIS的总体思想&#xff0c;以及MYSQL等技术的支持下共同完成了该平台的开发&#xff0c;实现了企业客户信息反馈管…

标准库中的string

字符串容量头文件&#xff1a;<string> string本质上就是字符顺序表; class string { private:char* str;size_t _size;size_t capacity; }; 1. string类对象的常见构造 (constructor) 函数名称 功能说明 string() &#xff08;重点&#xff09; 构造空的 string 类…

为你的世界,开一扇任意门(上篇)

一、每个侠客心中都有一扇任意门 童年时代就喜欢《多来A梦》&#xff0c;特比羡慕和渴望有一扇任意门&#xff0c;可以穿越到目的地&#xff0c;真正做到虽万里路&#xff0c;咫尺间。 随着技术及姿势的普及&#xff0c;基本有点常识的朋友都知道快捷方式&#xff0c;再略微熟…

手机三要素接口怎么对接呢?(一)

一、什么是手机三要素&#xff1f; 手机三要素又叫运营商三要素&#xff0c;运营商实名认证&#xff0c;运营商实名核验&#xff0c;手机三要素实名验证&#xff0c;手机三要素实名核验&#xff0c;每个人的称呼都不同&#xff0c;但是入参和出参是一样的。 输入姓名、身份证…

谁骑车没点心情件呀|VELO Skin Wrap把带,你的骑行显眼带!

夏日的热浪在召唤&#xff0c;是时候给你的骑行加点色彩&#xff0c;添点心情件呀&#xff01;夏季骑行&#xff0c;最怕的就是手心湿漉漉的不适&#xff0c;由此生发&#xff0c;VELO Skin Wrap系列把带&#xff0c;以天鹅绒材质精心打造&#xff0c;如同指尖上的夏日微风&…

MES系统在机床产业智能化的作用

MES系统&#xff08;Manufacturing Execution System&#xff0c;制造执行系统&#xff09;在机床产业智能化过程中发挥着至关重要的作用。以下是万界星空科技MES系统在机床产业智能化中的几个关键作用&#xff1a; 1. 实时数据采集与分析 数据采集&#xff1a;MES系统通过与生…

人工智能和计算机视觉领域国际学术会议submission

文章目录 1. AAAI 20252. CVPR 20253. ICCV 20254. IJCAI 20255. ICRA 20256. NeurIPS 20257. ACL 20258. ICLR 2025 1. AAAI 2025 人工智能促进协会&#xff08;AAAI&#xff09;是一个成立于1979年的非营利性科学组织&#xff0c;专注于深化对智能行为和思维机制的科学理解&…

暄桐好作业之《临沈周〈东庄图册〉局部》

暄桐是一间传统美学教育教室&#xff0c;创办于2011年&#xff0c;林曦是创办人和授课老师&#xff0c;教授以书法为主的传统文化和技艺&#xff0c;皆在以书法为起点&#xff0c;亲近中国传统之美&#xff0c;以实践和所得&#xff0c;滋养当下生活。      其中“暄桐好作…