什么是左值、右值呢?左右值和左右值引用

1. 左右值和左右值引用什么是左值、右值呢? 一种极不严谨的理解为:在赋值的时候,能够被放到等号左边的值为左值,放在右边的值为右值。例如:
int sum(int x, int y){return x + y;}int a = 1; //a为左值,常数1为右值int b = a + a; //b为左值,表达式a+a为右值int c = sum(a, a);//c为左值,但函数sum(a, a)返回值为右值通过上面的例子,常数a、表达式(a+a)和函数sum(a+a)返回值他们都是临时值,这些值都保存在寄存器中,无法取到他们的地址;而对于a、b和c为具体的变量名,存储在内存中,可以取到其地址。因此一般情况下可以根据能否取到地址,来区分左值和右值。
在了解左值和右值之前,我们首先要知道表达式的概念: 由运算符和运算对象构成的计算式(类似数学中的算术表达式) 。表达式是可以求值的,因此根据表达式值的类别,可以对其进行分类(准确的来说,是表达式的结果的值类别,但我们一般不刻意区分表达式和表达式的求值结果,所以这里称“表达式的值类别”。),c++11之后将表达式定义了五种类型:
lvalue (left-hand-side value,左值)prvalue (pure rvalue,纯右值)xvalue (expiring value,将亡值)rvalue (right-hand-side value,右值)glvalue (generalized lvalue,泛左值)它们之间的关系如下图所示:
c++11中将表达式按值类别可以分为 左值 、将亡值和 纯右值 。其中,左值和将亡值合称为泛左值,纯右值和将亡值合称为右值。
随着移动语义(后面我们会详细介绍)引入到 c++11 之中,值类别被重新进行了定义,c++之父bjarne stroustrup在《“new” value terminology》中给出以区别表达式的两种独立的性质:
拥有身份 (identity):可以确定表达式是否与另一表达式指代同一实体,例如通过比较它们所标识的对象或函数的(直接或间接获得的)地址;可被移动 :移动构造函数、移动赋值运算符或实现了移动语义的其他函数重载能够绑定于这个表达式。c++11 中:
拥有身份且不可被移动的表达式被称作 左值 (lvalue)表达式;拥有身份且可被移动的表达式被称作将****亡值 (xvalue)表达式;不拥有身份且可被移动的表达式被称作 纯右值 (prvalue)表达式;1.1 左值一般情况下,左值我们可以简单地理解理解为: 能够使用&取地址的表达式 。
常见的左值有:
变量名函数名返回左值引用的函数调用前置自增/减的运算符链接的表达式(如++i/--i)内置的赋值表达式(如a=b,a+=1)字符串等。【 注 :字符串是可以取地址的,因此字符串常量也属于左值】
1.2 纯右值纯右值:表达式本身就是纯粹的字面值(如1,ture,1.0);或者,该表达式求值结果相当于一个字面值或一个不具名的临时对象。
常见的纯右值有:
除字符串字面值以外的字面值返回非引用类型的函数调用后置自增/减的运算符链接的表达式(如i++/i--)算术/逻辑/比较表达式(如a+b,a&&b,a==b)取地址表达式(如&a)1.3 将亡值将亡值是在c++11中引进来的,顾名思义,就即将销毁的东西。将亡值的产生与右值引用的产生而引起的,对于将亡值我们常用到的有:
返回类型是右值引用的函数调用或重载运算符的表达式(如std::move(x))转换为右值引用的转换函数的调用表达式(如static(a))1.4 左右值引用左值引用就是对左值的引用。它的形式如:t&,根据const属性可以分为两种:
const左值引用非const左值引用例如:
int a = 1;int& la = a;//la为a的左值引用(非const左值引用)la = 2;//la为非const左值引用,可以修改它的值const int& c_la = a;//c_la为a的左值引用(const左值引用)c_la = 2;//该语法错误,c_la为const左值引用,不可以修改它的值右值引用就是对右值的引用,通过t&&来表示。右值的引用只能绑定到右值上。
2. 移动语义在未出现右值引用之前,我们在函数调用传参的时候,在某些时候可以使用按引用传递参数,减少参数多的拷贝对资源的消耗,提高程序的运行效率。当我们在处理包含大量数据的对象时,移动语义显的尤为重要。
2.1 std::move如何将一个左值转换为一个右值呢?c++11在头文件utility中声明了std::move()函数,该函数的作用就是类型转换,通过它,我们可以 把一个左值,将其标记为右值。move()不做任何资源转移的操作,只是产生一个将亡值表达式来标识参数x,其完全等同于static_cast(x)。例如:
int a = 1;int&& r_a = a; //错误,右值引用只能绑定到右值上,而a是一个左值int&& r_a = std::move(b); //正确, std::move(a) 是一个右值,可以用右值引用绑定2.2 移动构造函数一个类 t 的首个形参是 t&&、const t&&、volatile t&&或 const volatile t&&,且没有其他形参,或剩余形参都有默认值。
具体的形式如下:
t (t &&) //移动构造函数的典型声明形式t (t &&) = default; //强制编译器生成移动构造函数。t (t &&) = delete; //避免隐式生成移动构造函数。示例:
#include #include #include class a{ private: std::string s; public: a(std::string str = a()) : s(str) {std::cout<2.3 移动赋值运算符一个类 t 的移动赋值运算符是名为 operator=的非模板非静态成员函数,它接受恰好一个 t&&、const t&&、volatile t&&或 const volatile t&& 类型的形参。
具体的形式如下:
t & t ::operator= (t &&) //移动赋值运算符的典型声明t & t ::operator= (t &&) = default; //强制编译器生成移动赋值运算符t & t ::operator= (t &&) = delete; //避免隐式移动赋值示例:
#include #include #include class a{ private: std::string s; public: a(std::string str = a()) : s(str) {std::cout<

EV3000变频器的调速监控系统设计
TDK采用混合聚合物技术的轴向电容器的特点
IBM发布混合云时代新系统解决方案
无线人体局域网标准“BAN(IEEE 802.15.6)”正式批准
详解I2C串行通讯总线
什么是左值、右值呢?左右值和左右值引用
深入剖析开关电源的电感电流选择
关于无人机的“空中物流”的盛行
基于FPGA芯片实现单对差分线串行传输系统的设计
奥凯航空长龙航空等十三家航空公司正式结成维修联盟
AMetal开发通用外设的流程与规范
美国首条太阳能公路正式投运
机械制图的公差与配合及其标注方法
声音发生器电路图分享
可对音量进行联动调节的高低音提升电路
森思泰克荣获神龙汽车2022年度杰出开发奖!
川土微电子发布具有唤醒功能的四通道LIN收发器
LPO是什么?LPO作用是什么
汽车OTA升级是未来智能化汽车时代的必然选择
鸿蒙p20什么时候升级