1、左值和右值

左值(left-values),缩写:lvalues  ,located value 可定位值,其含义是可以明确其存放地址的值,更确切说对其的使用是基于地址

右值(right-values),缩写:rvalues , read value 可读的值,通常指代赋值运算=右侧的常量值,字面值,或者函数的返回值,它们没有具体的指代名,即无法通过地址访问,通常在赋值表达式结束后变销毁。

一般可以认为:左值对应变量的地址,右值对应变量的值,首先说左值和右值,他们绝不是简单的等号左边和右边的区别,总结来说:

  • 左值可以寻址,而右值不可以。
  • 左值可以被赋值,右值不可以被赋值,可以用来给左值赋值。
  • 左值可变,右值不可变(仅对基础类型适用,用户自定义类型右值引用可以通过成员函数改变)。
int value=fun();

最后c++11中还有一个将亡值的概念,是c++11中新增的跟右值引用相关的表达式,这样的表达式通常是将要被移动的对象


2、左值引用和右值引用

左值引用是对值的引用类型,用     T & a    来表示

右值引用是对值的引用类型,用     T &&a   来表示

左值引用和右值引用同为引用,他们在声明的同时必须被初始化(引用语法规定

不要混淆 取地址 和 引用,当&说明符前面带有类型声明,则是引用,否则就是取地址(必须是声明以及初始化),通俗来说 &在 ”=” 号左边的是引用,右边的是取地址。

左值引用:也就是通常所述的引用,引用是原先左值的别名,在定义时完成初始化,并且不可以作变更。左值引用和左值共同使用内存中同一份内容数据

const int&a = 1 //常量左值引用是可以绑定右值的
const int a = 1 //从语法上讲后者的右值在表达时结束后就销毁了,而前者不会

函数在传值方式传递参数,以及返回函数值(例如返回类型为复杂类类型(class)),都会执行拷贝构造,将带来大量无畏的拷贝构造开销。

这就是我们尽量用const 引用代替值传递做函数参数的原因,它在某种程度上可以提高效率

右值引用:即(Move Semantics:移动语义),避免无意义的拷贝赋值操作,C++11提出右值引用的概念,其本质是接替右值的所有权(不销毁原先的内存内容,而是将所有权移交给被交付的对象。)。

int &&value = 1    ;//右值引用一个常量值
int &&value = fun();//右值引用一个临时的函数返回值
  • 相对于左值,右值的生命周期很短,如函数的临时返回值,可以安全的转移控制权
  • 将右值的资源不释放,而是采取右值引用的方式继续使用,减少大量的拷贝,复制带来的开销。
  • 编译器会默认开启返回值优化,解决重复对象构造问题,而采取右值引用可以语言层面实现。

C++11引入的右值引用带来了深刻的性能提升,改变了以往复制,拷贝的繁琐操作,转而采取更为智能的移动技术,其本质通过移交所有权,在不改变内存内容情况下,完成资源的转移。

例如源码STL中的,vector的扩容使用到了右值引用来提高效率:

当Vector的size增大到capacity的一定比例后,需要申请一片更大的内存空间,同时将原先的数据转移到新空间。在移动技术之前,这意味着大量元素的深拷贝,而采取移动技术,无需释放原先空间,而只需要将原先空间所有权交由给新空间创建者即可。

额外知识点深拷贝、浅拷贝:https://blog.csdn.net/u014430031/article/details/115383480

总结生面两段话,左值引用和右值引用座位函数参数都能避免对象的拷贝和构造

但是我们通过右值引用改变一个右值时是没有意义的,而我们通过左值引用改变一个左值是有意义的。


3、Perfect Forwarding:完美转发(move、forward函数

实际上std::move就是一个类型转换器,将左值转换成右值而以。我们来看一下它的实现吧!

template <typename T>
typename remove_reference<T>::type&& move(T&& t)
{return static_case<typename remove_reference<T>::type&&>(t);
}

通用引用

首先我们来看一下move的输入参数,move的输入参数类型称为通用引用类型。什么是通用引用呢?就是它既可以接收左值也可以接收右值。

代码中有两种类型的通用引用: 一种是auto,另一种是通过模板定义的T&&。实际上auto就是模板中的T,它们是等价的。

模板的类型推导

通用引用好强大呀!它既可以接收左值又可以接收右值,它是如何做到的呢?这就要讲讲模板的类型推导了。

代码中分为两种类型:基本类型引用类型

基本类型:不带地址的普通原始类型。

引用类型:存储的值是地址,指向存放数据的位置。

模板的类型推导规则还是蛮复杂的,这里我们只简要说明一下,有兴趣的同学可以查一下C++11的规范。我们还是举个具体的例子吧:

template <typename T>
void f(T param);  //第一类//下面是传值的模板,由于传入参数的值不影响原值,所以参数类型退化为原始类型
int x = 10;         // x是int
const int& rx = x;  // rx是const int &
f(rx);              // T是int

对于第一类其推导时根据的原则是,函数参数传值不影响原值,所以无论你实际传入的参数是普通变量、常量还是引用,它最终都退化为不带任何修修饰的原始类型。

template <typename T>
void func(T& param); //第二类template <typename T>
void function(T&& param); //第三类//下面是传引用模板, 如果输入参数类型有引用,则去掉引用;如果没有引用,则输入参数类型就是T的类型
int x = 10;         // x是int
const int& rx = x;  // rx是const int &
func(x);            // T为int
func(rx);           // T为const int//下面是通用引用模板,与引用模板规则一致
function(x);        // T为int&
function(5);        // T为int

第二类为模板类型为引用(包括左值引用和右值引用)或指针模板。这一类在类型推导时根据的原则是去除对等数量的引用符号,其它关键字照般。还是我们上面的例子,func(x)中x的类型为 int&,它与T&放在一起可以知道T为int。另一个例子function(x),其中x为int&它与T&& 放在一起可知T为int&

返回类型

我们来看一下move的remove_reference看着很陌生,接下来我们再分析一下remove_reference类,看它又起什么作用吧。其实,通过它的名子你应该也能猜个大概了,就是通过模板去除引用。我们来看一下它的实现吧。

template <typename T>
struct remove_reference{typedef T type;  //定义T的类型别名为type
};template <typename T>
struct remove_reference<T&> //左值引用
{typedef T type;
}template <typename T>
struct remove_reference<T&&> //右值引用
{typedef T type;
}

通过上面的代码我们可以知道,经过remove_reference处理后,T的引用被剔除了。假设前面我们通过move的类型自动推导得到T为int&&,那么再次经过模板推导remove_reference的type成员,这样就可以得出type的类型为int了。

remove_reference利用模板的自动推导获取到了实参去引用后的类型。现在我们再回过来看move函数的时候是不是就一目了解了呢?之前无法理解的5行代码现然变成了这样:

int && move(int&& && t){return static_case<int&&>(t);
}//或
int && move(int& && t){return static_case<int&&>(t);
}

经上面转换后,我们看这个代码就清晰多了,从中我们可以看到move实际上就是做了一个类型的强制转换。如果你是左值引用就强制转换成右值引用。

引用折叠

上面的代码我们看起来是简单了很多,但其参数int& &&int && &&还是让人觉得很别扭。因为C++编译器根本就不支持这两种类型。咦!这是怎么回事儿呢?

查看一下引用折叠规则:

   
Expanded type Collapsed type
T& & T&
T& && T&
T&& & T&
T&& && T&&

总结一句话就是左值引用总是折叠为左值引用,右值引用总是折叠为右值引用。

forward的作用

std::forward被称为完美转发,它的作用是保持原来的属性不变。啥意思呢?通俗的讲就是,如果原来的值是左值,经std::forward处理后该值还是左值;如果原来的值是右值,经std::forward处理后它还是右值。

forward实现原理

要分析forward实现原理,我们首先来看一下forward代码实现。由于我们之前已经有了分析std::move的基础,所以再来看forward代码应该不会太困难。

……template <typename T>
T&& forward(typename std::remove_reference<T>::type& param)
{return static_cast<T&&>(param);
}template <typename T>
T&& forward(typename std::remove_reference<T>::type&& param)
{return static_cast<T&&>(param);
}……

forward实现了两个模板函数,一个接收左值,另一个接收右值。在上面有代码中:

typename std::remove_reference<T>::type

的含义我们在分析std::move时已经向你做了说细的说明,其含义就是获得去掉引用的参数类型。所以上面的两上模板函数中,第一个是左值引用模板函数,第二个是右值引用模板函数。

紧接着std::forward模板函数对传入的参数进行强制类型转换,转换的目标类型符合引用折叠规则,因此左值参数最终转换后仍为左值,右值参数最终转成右值。

总结:

右值引用将左值与右值区分开来。它们可以帮助您通过消除不必要的内存分配和复制操作来提高应用程序的性能。它们还使您能够编写接受任意参数的函数的一个版本,并将其转发给另一个函数,就好像直接调用了另一个函数一样

C++面试 左值、右值、左值引用、右值引用相关推荐

  1. 笛卡尔树 (25 分)笛卡尔树是一种特殊的二叉树,其结点包含两个关键字K1和K2。首先笛卡尔树是关于K1的二叉搜索树,即结点左子树的所有K1值都比该结点的K1值小,右子树则大。其次所有结点的K2关键字

    立志用最少的代码做最高效的表达 笛卡尔树是一种特殊的二叉树,其结点包含两个关键字K1和K2.首先笛卡尔树是关于K1的二叉搜索树,即结点左子树的所有K1值都比该结点的K1值小,右子树则大.其次所有结点的 ...

  2. 左值引用——右值引用 详解

    顾明思议 左值引用 就是对左值的引用 就是给左值取别名 右值引用 就是对右值的引用 就是给右值取别名 当改变别名是 该值也相应的改变 那么 何以区分哪些是左值哪些是右值呢? 左值 右值 在内存中有特定 ...

  3. 判断是否左值引用/右值引用

    判断是否左值引用/右值引用 有时候搞不清楚推导出来的类型是左值引用还是右值引用,可以用接口辅助判断: int i = 0; std::is_lvalue_reference<decltype(+ ...

  4. C++11中的右值引用(对比左值引用和常引用)、移动构造函数和引用标识符

    Hello!各位同学们大家好!逗比老师最近说起来还是挺尴尬的,为什么这么说呢?因为以前我对自己的C++水平还是相当自信的,经常以"精通"来自我评价.但是最近发现自己好像对C++11 ...

  5. 深入浅出C++左值引用,右值引用,移动语义。

    什么是左值 右值? 简单来说左值就是可以取地址,在=左边的,而右值就是不可以取地址,在=右边的. int t=10; t可以通过&取地址在=左边 所以t是左值 10不可以取地址 在=右边10是 ...

  6. c++ | 左值引用 右值引用

    左值:可以取地址的.有名字的就是左值,比如 int a; 右值:不能取地址的是右值,表达式结束后就会被销毁, 比如 a*3 左值引用:就是普通引用,是为对象起的别名,必须被初始化,与变量绑定到一起 如 ...

  7. [c++]-c++中的左值和右值、左值引用和右值引用、万能引用和引用折叠及完美转发

    1.左值和右值 1.1左值和右值定义 在c++中,左值是一个指向内存的东西,换句话来讲,左值有地址,保存在内存中,右值则为不指向任何地方东西,即不在内存中占有确定位置.一般来说,右值是暂时和短暂的,而 ...

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

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

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

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

  10. 2020-09-22C++学习笔记之引用1(1.引用(普通引用)2.引用做函数参数 3.引用的意义 4.引用本质5.引用结论 6.函数返回值是引用(引用当左值)7测试代码)

    2020-09-22C++学习笔记之引用1(1.引用(普通引用)2.引用做函数参数 3.引用的意义 4.引用本质5.引用结论 6.函数返回值是引用(引用当左值)7测试代码) 1.引用(普通引用) 变量 ...

最新文章

  1. ffmpeg php 抠像_PHP中使用ffmpeg截取视频图片笔记
  2. 专家点评Nature Plants | 中科院微生物所郭惠珊研究组揭示土传病原菌逃避寄主免疫的新机制...
  3. webpack2诸类事宜
  4. hdu1787-GCD Again
  5. 【杂谈】GAN对人脸图像算法产生了哪些影响?
  6. mybatis动态调用表名和字段名
  7. 2018.11.07-1015-幸运字符串查询 (lucky)
  8. py2topy3+cmd 命令
  9. ad15的stc元件库_STC8系列 STC15系列STCMCU Protel_Altium原理图PCB器件封装库文件
  10. 精准营销的神器:小蜜蜂获客系统助你成功
  11. C语言-用栈实现表达式求值
  12. 关于win10主机共享打印机,其他主机连接不上的解决方法①
  13. Qt5--学习笔记-+openCV2-客户端、服务端回环视频显示
  14. 微信小程序image组件频闪问题
  15. 平板电脑4g运行内存够用吗_如何通过Windows平板电脑与其他设备共享3G / 4G Internet连接...
  16. dreamweavercs5创建php,Dreamweaver cs5创建CSS规则的方法
  17. eclipse中设置java、xml文件的字体大小以及代码自动提示功能
  18. 多因子选股模型python_量化交易——因子选股、多因子选股策略
  19. 在机箱里 碰一下机箱就重启_使用机箱进行WordPress开发
  20. 如何用Java来进行文件切割和简单的内容过滤

热门文章

  1. 邓_Jquery测试题
  2. C memset() 函数
  3. 2022爱分析· RPA厂商全景报告 | 爱分析报告
  4. 【H3C V7路由器实战视频课程系列-8】IS-IS路由配置与管理-王达-专题视频课程
  5. DBShop后台RCE之曲线救国
  6. JSP使用Websocket技术实现聊天功能--H5网页前端部分(二)
  7. Java数据结构与算法———(55)创建一个哈希表
  8. 什么是物联网?用i.MX8M mini 打造物联网网关
  9. WEB阶段3:Response响应组成常见状态码ServletContexturl编码文件下载案例
  10. Android培训Android课堂重点内容汇总