C++11最广泛的特性是移动语义,移动语义的基础在于区分左值表达式和右值表达式。一般来说,一个对象是右值意味着可以对其实施移动语义,而左值不可以。右值对应的是函数返回的临时对象,而左值对应的是可指涉的对象,指涉途径有:标识符、指针、左值引用等。

常见的疑问有:

(1)什么是左值和右值?

(2)什么是左值表达式、右值表达式?

(3)引用有哪些?什么又是左值引用和右值引用?

1、一种简明的说法是:

在赋值运算法左边的就是左值,右边的就是右值;

表达式求值结果是左值,就是左值表达式,表达式求值结果是右值,就是右值表达式;

  1. 这种简明的说法不严谨不准确,以下给出详细说明:

表达式是由运算符或运算对象构成的计算式,字面值(literal)和变量(variable)是最简单的表达式,函数返回值也是表达式,而表达式是可以求值的,对表达式求值将得到一个结果(result),这个结果/值/量有两个自带的属性:型别和值类别。类型大家都很容易理解,int、char、reference...,但是值类别可能容易被忽视,而这最容易被忽视和不知的点:值类型正是C++11后划分对象/值/量的根据和基础。

C++11标准中规定复杂,对于值/表达式的解释大概划分如下:

具体说明如下表:

Expression / value

表达式 / 值

Lvalue

[左值]

rvalue

[右值]

glvalue

[广义左值/泛左值]

xvalue

[将忘值]

prvalue

纯右值

(1)Prvalue纯右值【纯纯的沙雕】

定义

无标识符、不可取地址的表达式/对象,占内存但立即释放

(也叫临时对象)

纯右值[属于右值]

字面值(字串字面值除外)

1,‘w’,nullptr,true...

返回值为非引用的函数调用

str.substr(1, 2), str1 + str2, or it++

后置自增自减表达式

a++, a–

算术表达式

a + b

逻辑表达式

a && b

比较表达式

a > b

取地址表达式

&a

Lambda表达式

...

(2)Lvalue左值【左盟主】

定义

有标识符、可取地址的表达式/对象

左值[属于广义左值]

字串字面值

“hello world” ...

变量、函数、数据成员名字

int a, fun(), str.m_name

前置自增自减表达式

++a, --a

返回左值引用的表达式

x = 1、cout << ’ ’,...

注意:

在函数调用时,左值可以绑定到左值引用的参数,如 T&。

一个常量只能绑定到常左值引用,如 const T&。

左值和右值都是针对表达式而言的(也就是说二者不存在严格的区别):

左值是指表达式结束后依然存在的持久对象

右值是指表达式结束时就不再存在的临时对象
C/C++规定:对于对象的引用必须是左值(常量引用除外)
const引用能够绑定到临时对象,

并将临时对象的生命周期由”创建临时对象的完整表达式”提升

至”绑定到的const引用超出作用域” non-const 引用没有这个功能。

示例:
const int& a = 101;   //对{常引用可以作用于右值}
int& b = 101;            //错{左值引用不可引用右值}
int a;
int &b = a;          //对{左值引用可引用左值}
a = 10;
printf(“b = %d\n”,b);
此时b = 10,b是a的引用,就是说b和a的地址相同,对a做改变b也跟着变化。

  1. Xvalue将亡值【右值引用引出】

定义

通过右值引用符号“&&” ,

返回右值引用的函数调用表达式,

转换为右值引用的转换函数的调用表达

将亡值[右值引用/广义左值]

T&& 函数返回值

T&& fun();

移动语义返回值

std::move、tsatic_cast<X&&>(x)

具有右值引用型别的左值

在C++11中,用左值去初始化对象或为其赋值,会调用拷贝构造函数或拷贝赋值运算符函数来拷贝资源,用右值(纯右值和将亡值)初始化和赋值时,调用移动构造函数或移动赋值运算符来移动资源,这样会避免拷贝,提高性能效率,右值完成移动后会马上销毁(析构)。

这样的右值存在的使命就是移动后走向死亡,所以叫做“将亡值”。

  1. 五种值类型辨析

辨析内容

左值

右值

自增自减(看符号)

++i

i--

解引用和取地址(解左取右)

*p

&a

常见表达式

x = 1、cout << ’ ’

a+b、a&&b、a==b

字面值(字串为左值)

“ddd”

1、3.14、‘r’

具名和不具名

具名的右值引用

不具名的右值引用

void foo(int&& t) t

(5)值类别与型别的辨析

值类别

型别

左值、纯右值、将亡值

内建类型、基本类型(**指针、**引用...)、自定义类型

表达式的型别与他是左值和右值没有关系[一个是型别,一个是值类别],很多的移动构造函数内部对入参取地址完全没问题,尽管该参数的型别属于右值引用。

基于此,我们可以得知,任何形参都是左值

当一个对象被用作右值的时候,用的是对象的值(内容);

当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。

大概的意思就是说左值就有内存地址的,存活的生命周期较长的,

而右值一般是无法获取到内存地址的,生命周期是短暂的。还是以以上的代码为例子,

  1. 引用型别可分为四类

(1)左值引用:即绑定到左值的引用,必须绑定左值

int i = 1;

int &ref = i; //这里ref是绑定了i这个左值的引用

int &ref2 = 3; // 3 是右值,无法绑定

(2)右值引用

右值引用的作用是给开发者提供一个想要可以绑定临时变量的作用,可以通过右值引用符号“&&” 来实现

int &&i=1;//右值引用可直接引用右值,想要给 “ 1 ”一个固定地址,并给予一个变量名i

i = 3;//可以通过i再赋值

右值引用b延长了函数getX返回值的生命周期。延长临时对象生命周期并不是这里右值引用的最终目标,其真实目标应该是减少对象复制,提升运行性能(关闭RVO优化

右值---移动语义

移动构造

对于拷贝构造函数而言形参是一个左值引用,而不能是某些函数返回的临时对象,而且在拷贝构造函数中往往进行的是深复制,即一般不会破坏实参对象。而移动构造函数恰恰相反,它接受的是一个右值,其核心思想是通过转移实参对象的数据以达成构造目标对象的目的,也就是移动构造函数会修改实参对象,一般来说调用了移动构造函数之后,实参对象的相关变量资源就会被转移,原本实参的变量就会被置空,也就是实参就不能再使用了, 因此与其叫做移动构造函数不如叫做窃取构造函数更加的贴切。

那么在什么情况会发生移动构造的调用呢?比如在C++11的STL容器中,会根据具体情况自动调用移动构造函数,比如以下例子:

注意:和拷贝构造函数对于拷贝赋值运算符一样,移动构造函数也对于这一个移动赋值运算符,因为在移动语义中一般会置空实参的相关变量,所以需要注意在移动赋值运算符避免自己赋值给自己的情况这样会给自己赋值的同时置空自己,在做无用功。

(3)Const常量引用,本质上也是左值引用的一种,但区别有二

一是无法通过这个引用改变引用地址的值【只读】,

二是它可以间接绑定右值(实际上是绑定了一种左值)【绑定右值】

int i = 1;

const int &ref = 1;

ref  = 10;   //错误,无法再赋值 ,因为ref是一个常量引用

const int &ref2 = 100;//可引用一个右值,本质上是100转换为一个变量,

// 再将ref2引用到这个变量上

总结:

左值引用只能引用左值,右值引用只能引用右值,

而const引用就可以同时引用左值和右值

(通过临时变量间接引用右值,实际上是左值引用)

(4)万能引用

令人眼花的是这个将亡值,它既可以代表一个左值,又可以代表一个右值,这是怎么的一回事呢?所谓的万能引用就是既可以引用左值,也可以引用右值的引用,

一般是在模板中的未知右值引用类型,需要根据规则进行推导型别的引用。

只要发生了类型推导就会是万能引用,在T&&和auto&&的初始化过程中都会发生类型的推导所以它们是万能引用。在这个推导过程中,初始化的源对象如果是一个左值,则目标对象会推导出左值引用;反之如果源对象是一个右值,则会推导出右值引用。

通过类型推导细分万能引用,关于万能引用主要涉及到引用折叠规则(类型推导)、完美转发两个方面。

完美转发

上面介绍了万能引用,它的一个重要用途就是进行完美转发,所谓完美转发指的是函数模板可以将自己的参数“完美”地转发给内部调用的其它函数,不仅能准确地转发参数的值,还能保证被转发参数的左、右值属性不变。

在C++11使用标准库中的std::forward函数就可以试下完美转发:

参考博文:C++之右值引用_FlyerGo的博客-CSDN博客_c++右值引用

移动语义引发的思考之左值、右值相关推荐

  1. C++11新特性之左值右值及移动语句与完美转发

    C左值右值 左值和右值的由来 什么是左值和右值 左值右值的本质 引用 左值引用 右值引用 移动语句与完美转发 移动语句 实现移动构造函数和转移赋值函数 stdmove 完美转发Perfect Forw ...

  2. c++ 左值 广义左值 右值 纯右值 将亡值

    为什么C/C++等少数编程语言要区分左右值? 历史原因: C语言作为一门古老的编程语言,其设计初衷是为了在硬件资源有限的系统上进行高效的编程,因此其语法和语义设计相对较简单.左值和右值的概念最初是由C ...

  3. 表达式左值右值(C++学习)

    左值右值是表达式的属性,该属性称为 value category.按该属性分类,每一个表达式属于下列之一: lvalue left value,传统意义上的左值 xvalue expiring val ...

  4. 左值/右值/左值引用/右值引用/move的用法介绍

    目录 问题 左值和右值 概念总结: 需要用到左值的运算符: 引用分类 左值引用 右值引用 右值引用到底什么用? std::move()函数介绍 问题 什么是左值和右值? 什么是左/右值引用? 左/右值 ...

  5. std::move 左值右值 左值引用右值引用

    参考:https://blog.csdn.net/daaikuaichuan/article/details/88371948 https://zhuanlan.zhihu.com/p/9458820 ...

  6. C++基础知识(二)--左值右值--逻辑表达式求值优化--逗号运算符与表示式--输入输出格式控制...

    :一.C++左值右值概念 左值:c++将变量名代表的单元称为左值,而将变量的值称为右值,左值必须是内存中可以访问且可以合法修改的对象,因此只能是变量名,而不能是常量或表达式.即左值可以寻址. 右值:将 ...

  7. C、C++差异之左值右值

    C与C++在语法细节上还是有一些差异的,虽然一般情况下可能这些差异不足以造成结果的区别,但有些代码确实会有影响. 这次,主要总结下左值右值的差异. 在C中,很多左值运算符的结果都不再是左值,然而在C+ ...

  8. 左值右值将亡值泛左值

    左右值概念 简单理解 左值:赋值运算符左边的变量,可以接受右边值,例如 int a = 10; a就是一个左值右值:赋值运算符右边的值,这个值可以是一个变量也可以是一个常量,例如 int a = 10 ...

  9. C++易被忽略的知识点:移动语义 左值右值

    目录 lvalue 和 rvalue rvalue 引用 移动语义 移动语义的概念 强制移动 lvalue 和 rvalue 每个表达式都会得到 lvalue 或 rvalue.它们的区别是,lval ...

最新文章

  1. html 颜色叠加图片,如何在背景图片上添加颜色叠加?
  2. 通过注册表开启“终端服务”
  3. selenium python_Python+Selenium基础入门及实践
  4. 团队-科学技术器-模块测试过程
  5. [PHP]对Json字符串解码返回NULL的一般解决方案
  6. java实现数据库内容修改_数据库更改到Java环境中实现可持续和平
  7. linux无filelength函数,Linux Shell 自定义函数(定义、返回值、变量作用域)介绍
  8. JDK源码解析之 java.lang.Integer
  9. Discuz素材资源下载官网门户+自带论坛 整站源码+带后台+带数据库
  10. 毕设日志——Fast RCNN
  11. python如何下载安装tensorflow_TensorFlow下载与安装
  12. Java集合详解2:LinkedList和Queue
  13. Atitit wsdl的原理attilax总结
  14. MySQL不同字段比较大小_mysql 字段定义 对 大小比较的影响
  15. python实现银行ATM系统
  16. HMTL中隐藏域(type=hidden)
  17. GPS是如何定位的?
  18. 弘辽科技:淘宝开店可以卖不同类型产品吗?会有问题吗?
  19. python批量下载bilibili视频_关于bilibili视频下载的一些小思路
  20. CVE-2017-7494紧急预警:Samba蠕虫级提权漏洞,攻击代码已在网上扩散

热门文章

  1. video分享在微博或者微信的H5页面视频不能打开。
  2. 中国移动MCU CM32M101A-B128-LQFP48软硬件通用STM32F103CBT6
  3. 哪些原因是需要您备份网站的
  4. 大学生php开发培训,PHP开发 开发培训学费
  5. 程序员的浪漫——欲寄彩笺兼尺素,山长水阔知何处
  6. BZOJ2423/HAOI2010 最长公共子序列
  7. [ 1link.fun ] 1link.fun 第 23 期
  8. word、excel、PPT操作
  9. 【Spark】scala基础入门
  10. 鸿蒙OS是怎样实现跨平台的