移动语义引发的思考之左值、右值

本文深入探讨了C++11中的移动语义,重点阐述了左值和右值的区别,包括左值引用、右值引用的概念。移动语义利用右值提升性能,通过移动构造函数和移动赋值运算符有效地转移资源。此外,还介绍了常量引用和万能引用的角色,以及如何通过完美转发实现参数的精确传递。
摘要由CSDN通过智能技术生成

        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++右值引用 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值