C++ 右值引用与左值引用
意义:可以避免无谓的复制,提高程序的性能。
左值:表达式结束后依然存在的持久化对象
右值:表达式结束后不再存在的临时对象
所有的具名变量和对象都是左值,而右值不具名。
区分左值和右值的快捷方法:
看能不能对表达式取地址,如果能则是左值,否则就是右值。
右值分为纯右值和将亡值。
纯右值是C++98中的右值概念,如非引用函数返回的临时变量;
一些运算表达式,如4+6产生的临时变量;不和对象关联的字面量值,
如10,‘s’,true,“hello”等这些不能被取地址的值。
将亡值:c++11中新增的和右值引用相关的表达式,这样的表达式通常是将要移动的对象
T&&函数返回值,std::move()函数的返回值等。
将亡值和纯右值统一看成右值,不影响使用。
c++98中引用很常见,就是给变量取一个别名,在c++11中,因为增加了右值引用的概念,
所以c++98中的引用都称为左值引用。
int a = 10;
int &refA = a;
int &b = 1; //编译错误,1是右值,不能使用左值引用
c++11中的右值引用使用&&符号,如
int &&a = 1;
int b = 1;
int &&c = b; //编译错误,不能将左值赋值给一个右值引用
class A
{
public:int a;
};
A getTemp()
{return A();
}
A &&a = getTemp(); //getTemp()返回值是右值(临时变量)
getTemp()返回的右值本来在表达式语句结束后,其生命也就该终结了(临时变量),而通过
右值引用,该右值又获得了新生,其生命周期与右值引用类型变量a的生命一样,只要a活着,该
右值临时变量将会一直存活下去,实际上就是给临时变量去了一个名字。
a的类型为右值引用类型(int &&),如果从左值和右值的角度区分它,它实际上是一个左值。
因为可以对它取地址,而且它还有名字,是一个已经命名的右值。
所以,左值引用只能绑定左值,右值引用只能绑定右值。常量左值引用是个特例,它可以算一个
万能的引用类型,可以绑定非常量左值,常量左值,右值,而且在绑定右值的时候,可以像右值引用一样
将右值的生命期延长,缺点是只能读不能改。例子如下:
const int &a = 1; //常量左值引用绑定右值,不会报错
class A
{
public:int a;
};
A getTemp()
{return A();
}
const A &a =getTemp(); //不会报错,而A& a会报错
实际上,我们在很多情况下都使用了常量左值引用这个功能,例子如下:
class Copyable
{
public:Copyable() {}Copyable(const Copyable &o){std::cout << "Copied" << std::endl;}
};Copyable ReturnRvalue()
{return Copyable(); //返回一个临时对象
}void AcceptVal(Copyable a)
{
}
void AcceptRef(const Copyable &a)
{
}int main()
{std::cout<<"pass by value"<<std::endl;AcceptVal(ReturnRvalue()); //应该调用2次拷贝构造函数std::cout<<"pass by reference"<<std::endl;AcceptRef(ReturnRvalue()); //应该只调用一次拷贝构造函数
}
上述例子运行之后,结果和预想的不一样。AcceptVal(ReturnRvalue())需要调用两次拷贝构造函数,一次在ReturnRvalue()函数中,构造一个Copyable()对象,返回的时候会调用拷贝构造函数生成一个临时对象。在调用AcceptVal()时,会将这个对象拷贝给函数的局部对象a,一共调用了两次拷贝构造函数。而AcceptRef()的不同之处在于形参是常量左值引用,它能接收一个右值,而不需要拷贝。
实际的结果是,不管哪种方式,一次拷贝构造函数都没有调用。
这是因为编译器开启了返回值优化(RVO/NRVO,RVO,Return Value Optimization返回值优化;NRVO,Nameed Return Valude Optimization)。编译器发现ReturnRvalue内部生成了一个对象,返回之后还需要生成一个临时对象调用拷贝构造函数,很麻烦,所以直接优化成一个对象,避免拷贝,而这个临时变量又被赋值给了函数的形参,还是没必要,这3个变量都用一个变量代替了,不需要调用拷贝构造函数。
为了能够更好的观测结果,可以在编译的时候加上-fno-elide-constructors选项(关闭返回值优化),此时结果和预想的一样。上述的例子是想说明常量左值可以绑定一个右值,可以减少一次拷贝(使用非常量左值引用会使失败,因为ReturnRvalue()返回的是临时对象(右值))。
//g++ test.cpp -o test -fno-elide-constructors
总结:T是一个具体类型
(1)左值引用,使用T&,只能绑定左值
(2)右值引用,使用T&&,只能绑定右值
(3)常量左值,使用conts T&,可以绑定左值和右值。
(4)已命名的右值引用,编译器会认为是左值。
(5)编译器有返回值优化功能,但不可过于依赖。
参考:https://www.jianshu.com/p/d19fc8447eaa
C++ 右值引用与左值引用相关推荐
- 2020-09-22C++学习笔记之引用1(1.引用(普通引用)2.引用做函数参数 3.引用的意义 4.引用本质5.引用结论 6.函数返回值是引用(引用当左值)7测试代码)
2020-09-22C++学习笔记之引用1(1.引用(普通引用)2.引用做函数参数 3.引用的意义 4.引用本质5.引用结论 6.函数返回值是引用(引用当左值)7测试代码) 1.引用(普通引用) 变量 ...
- C++中“非常量引用的初始值必须是左值”的处理方法
原文:https://blog.csdn.net/hou09tian/article/details/80565343 1 左值和右值 在C++中,左值可以出现在赋值语句的左边和右边:右值只能出现在赋 ...
- 左值、左值表达式、左值引用 C++
左值和右值 1.左值:左值是一个对象或变量,可以代表着一个固定地址. int i = 3://此时,i是个变量,本质和对象一样,是一块内存区域,代表着一个固定的地址. 右值:不能作为左值的都是右值,要 ...
- 的引用_左值、右值、左值引用、右值引用
[导读]:本文主要详细介绍了左值.右值.左值引用.右值引用以及move.完美转发. 左值和右值 左值(left-values),缩写:lvalues 右值(right-values),缩写:rvalu ...
- 非常量引用的初始值必须是左值_C++核心编程--引用
2 引用 2.1 引用的基本使用 作用: 给变量起别名 语法: 数据类型 &别名 = 原名 示例: int main() {int a = 10;int &b = a;cout < ...
- java中的左值右值_利用左值右值实现树状结构
image.png 1. 查询 1.1. 得到节点 Node 下的所有节点,并按树状排序 SELECT * FROM tree WHERE lft BETWEEN Node.Lft AND Node. ...
- C++/C++11中左值、左值引用、右值、右值引用的使用
C++的表达式要不然是右值(rvalue),要不然就是左值(lvalue).这两个名词是从C语言继承过来的,原本是为了帮助记忆:左值可以位于赋值语句的左侧,右值则不能. 在C++语言中,二者的区别就没 ...
- 39.左值、左值引用、右值、右值引用
1.左值和右值的概念 左值是可以放在赋值号左边可以被赋值的值:左值必须要在内存中有实体: 右值当在赋值号右边取出值赋给其他变量的值:右值可以在内存也可以在CPU寄存器. ...
- 左值、右值、左值引用、右值引用
1. 左值 左值(lvalue,left value),顾名思义就是赋值符号左边的值,可以取地址.准确来说,左值是表达式(不一定是赋值表达式)后依然存在的持久对象. 可以将左值看作是一个关联了名称的内 ...
最新文章
- tf.contrib.layers.xavier_initializer
- ubuntu 配置网络
- NYOJ 73 比大小
- mysql 共享锁和排他锁 意向锁 记录锁 Gap Locks Next-Key Locks 插入意向锁介绍
- 【倍增】【线段树】雨林跳跃(luogu 7599[APIO 2021 T2])
- ES6的Promise -- 逻辑执行的顺序
- Qt5 程序启动画面图片效果
- 便携式CAN分析仪与毫米波雷达搭配使用
- 机器视觉Halcon教程(1.介绍)
- php自学多久可以上岗,自学php多久能找工作,PHP要自学多久才能找到工作
- 【APT】The following signatures were invalid
- 使用 mesh 实现多边形裁剪图片!Cocos Creator!
- 爆款“小黄鸭”之后,实在RPA助力电商人双11提销量
- 东华助手 v1.6.5
- 关于个人如何接入微信支付接口,适用于h5,小程序等应用场景
- 【GDOI2018模拟7.14】小奇的糖果
- 计算机重启后桌面文件全部丢失,蓝屏后桌面文件丢失,电脑蓝屏桌面文件丢失...
- linux 修改主机名 命令,Linux修改主机名命令详解
- PieLove(鹊桥·征男友女友) 版-内容抓取程序
- 【UE4 C++】如何关联GitHub 下载UE4引擎源代码
热门文章
- GDAL坐标转换——TransformPoint
- *1.设计一个异常类表示对负数求平方根的错误,在类Test的main方法中,* 从键盘中输入一个数字,若输入的数不小于0,则输出该数的平方根(用Math.sprt());* 若小于0,则抛出自定义
- Windows系统的消息机制
- maven下载(国内镜像)
- 华为2016校园招聘上机笔试题
- 如何在分割后保留分隔符?
- Centos 安装 OpenLDAP
- 哈尔滨傲澜智伴机器人_通辽智伴机器人,智伴机器人招商,傲澜智伴机器人
- flask 下载文件
- ios沙箱软件_iOS沙盒管理工具FHHSandBoxViewer