• c++ 左值,右值


    目录

    左值和右值的定义描述

    《c++程序设计语言》6.4

    《c++Primer》第五版中文版4.1.1

    左值引用和右值引用:

    《c++Primer》第五版中文版

    《c++程序设计语言》


    左值和右值的定义描述

    摘自两本书《c++程序设计语言》(Bjarne Strousstrup)和《c++Primer》第五版


    《c++程序设计语言》
    6.4

    我们可以分配并使用没有名字的对象(比如用new创建的对象),也能为某些看起来不太寻常的表达式赋值(如,*p[a+10]=7)。因此,我们需要用一个名字来表示“内存中的某些东西”。这就是对象一词最简单最基本的含义。 换句话说,对象(object)是指一块连续的存储区域,左值(lvalue, left value)是指向对象的一条表达式。“左值”的字面意思是“能用在赋值运算符左侧的东西”,但其实不是所有的左值都能用在赋值运算符的左侧,左值也有可能表示某个常量。未被申明成const的左值称为可修改的左值(modifiable lvalue)。

    6.4.1 左值和右值

            为了补充和完善左值的含义,我们相应地定义了右值(rvalue, right value)。简单来说,右值是指“不能作为左值的值”,比如像函数返回值一样的临时值。

    《c++Primer》第五版中文版
    4.1.1

     左值和右值
    c++的表达式要不然是右值(rvalue,读作"are-value”),要不然就是左值(lvalue,读作“ell-value”)。这两个名词是从c语言继承过来的,原本是为了帮助记忆:左值可以位于赋值语句的左侧,右值则不能。
            在c++语言中,二者的区别就没有那么简单了。一个左值表达式的求值结果是一个对象或者一个函数,然而以常量对象为代表的某些左值实际上不能作为赋值语句的左侧运算对象。此外,虽然某些表达式的求值结果是对象,但他们是右值而非左值。可以做一个简单的归纳:当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。
           
    不同的运算符对运算对象的要求各不相同,有的需要左值运对象、有的需要右值运算对象;返回值也有差异,有的得到左值结果、有的得到右值结果。一个重要的原则(参见13.6节,第470页将介绍一种例外的情况)是在需要右值的地方可以用左值来代替,但是不能把右值当成左值(也就是位置)使用。当一个左值被当成右值使用时,实际使用的是他的内容(值)。到目前为止,已经有几种我们熟悉的运算符是要用到左值的。

    •  赋值运算符需要一个(非常量)左值作为左侧运算对象,得到的结果也任然是一个左值。
    • 取地址符,作用于一个左值运算对象,返回一个指向该运算对象的指针,这个指针是一个右值。
    • 内置解引用运算符、下标运算符、迭代器解引用运算符、string和vector的下标运算符的求值结果都是左值。
    • 内置类型和迭代器的递增递减运算符作用于左值运算对象,结果也是左值。

    使用关键字decltype的时候,左值和右值也有所不同。如果表达式的求值结果是左值,decltype作用于该表达式(不是变量)得到一个引用类型。举个例子,假定p的类型是int*,因为解引用运算符生成左值,所以decltype(*p)的结果是int&。 另一方面,因为取地址运算符生成右值,所以decltype(&p)的结果是int**,也就是说,结果是一个指向整数型指针的指针。


    左值引用和右值引用:

    《c++Primer》第五版中文版

    13.6.1
    右值引用
    为了支持移动操作,c++新标准引入了一种新的引用类型--右值引用。 右值引用有一个重要的性质--只能绑定到一个将要销毁的对象。因此,我们可以自由地将一个右值引用的资源“移动”到另一个对象中。

    左值持久;右值短暂
            考虑左值和右值表达式的列表,两者相互区别之处就很明显了:左值有持久的状态,而右值要么是字面常量,要么是在表达式求值过程中创建的临时对象。
            由于右值引用只能绑定到临时对象,我们得知

    • 所引用的对象将要被销毁
    • 该对象没有其他用户

    这两个特性意味着:使用右值引用的代码可以自由地接管所引用的对象的资源。

    变量是左值
            变量可以看做只有一个运算对象而没有运算符的表达式,虽然我们很少这样看待变量。类似其他任何表达式,变量表达式也有左值/右值属性。变量表达式都是左值。带来的结果就是,我们不能将一个右值引用绑定到一个右值引用类型的变量上。  

    1. int &&rr1 = 42; // 正确:字面常量是右值
    2. int &&rr2 = rr1; // 错误: 表达式rr1是左值!

    其实有了右值表示临时对象这一观察结果,变量是左值这一特性并不惊讶。毕竟,变量是持久的,直至离开作用域时才被销毁。
    变量是左值,因此我们不能将一个右值引用直接绑定到一个变量上,即使这个变量是右值引用类型也不行。

    《c++程序设计语言》

    7.7

    引用:
    为了体现左值/右值以及const/非const的区别,存在三种形式的引用:

    • 左值引用(lvalue reference): 引用哪些我们希望改变值的对象
    • const 引用(const reference):引用哪些我们不希望改变值的对象(比如常量)
    • 右值引用(rvalue reference):所引对象的值在我们使用之后就无须保留了。(比如临时对象)

    7.7.1 左值引用
            在类型名字中,符号X&的意思是“X的引用”;它常用于表示左值的引用,因此称为左值引用。
     

    1. double& dr =1; //错误:此处需要左值
    2. const double& cdr{1}; // ok 这条语句可以理解为 : double temp = double{1}; 临时变量, const double& cdr{temp}; 用这个临时变量作为cdr的初始值。

    7.7.2 右值引用
            c++ 之所以设计了几种不同形式的引用,是为了支持对象的不同用法:

    • 非const左值引用所引的对象可以由用户写入内容。
    • const左值引用所引的对象从用户的角度来看是不可修改的。
    • 右值引用对应一个临时对象,用户可以修改这个对象(通常确实会修改它),并且认定这个对象以后不会被用到了。

    右边值引用使用: 3.3.2

    在移动构造函数中作为参数:

    1. class Vector{
    2. //...
    3. Vector(const Vector& a); //拷贝构造函数
    4. Vector& operator=(const Vector& a); // 拷贝赋值运算符
    5. Vector(Vector&& a); // 移动构造函数
    6. Vector& operator=(Vector&& a); // 移动赋值运算符
    7. };

    移动构造函数不接受const实参:毕竟移动构造函数最终要删除掉它实参中的值。移动赋值运算符的定义与之类似。  (在将对象作为函数返回值的时候,编译器将选择移动构造函数来执行从函数中移出返回值的任务。)

  • 相关阅读:
    “一万字”动静图生动结合详解:快速排序
    YOLOv9训练损失、精度、mAP绘图功能 | 支持多结果对比,多结果绘在一个图片(消融实验、科研必备)
    数组_滑动窗口 | leecode刷题笔记
    102.(前端)分类管理增加窗口显示——dialog弹窗的基本使用与在form表单显示变量
    SpringBoot + Redis的Bitmap实现活跃用户统计
    基于遗传算法的水力发电厂的优化(Matlab代码实现)
    常用注解归纳(二)
    XML Web 服务 Eclipse实现中的sun-jaxws.xml文件
    SpringBoot配置文件bootstrap与application (.yml .properties)
    springMVC02,restful风格,请求转发和重定向
  • 原文地址:https://blog.csdn.net/u012459903/article/details/127597238