C++ 左值、右值、左值引用、右值引用

前言

本文介绍C++11的各种引用的概念,理解清楚各种引用的概念,非常有助于理解基于c11引用的各种操作。

左右值概念

C++ 里有左值和右值,但C++按标准里的定义实际更复杂,规定了下面这些值类别(value categories):
在这里插入图片描述

  • 名词解释:
    一个 lvalue 是通常可以放在等号左边的表达式,左值
    一个 rvalue 是通常只能放在等号右边的表达式,右值
    一个 glvalue 是 generalized lvalue,广义左值
    一个 xvalue 是 expiring value,将亡值
    一个 prvalue 是 pure rvalue,纯右值

  • 左值 lvalue 是有标识符、可以取地址的表达式,最常见的情况有:
    变量、函数或数据成员的名字
    返回左值引用的表达式,如 ++x、x = 1、cout << ’ ’
    字符串字面量如 “hello world”
    在函数调用时,左值可以绑定到左值引用的参数,如 T&。一个常量只能绑定到常左值引用,如 const T&。

  • 纯右值 prvalue 是没有标识符、不可以取地址的表达式,一般也称之为临时对象,即临时对象就是一种右值,最常见的情况有:
    返回非引用类型的表达式,如 x++、x + 1、make_shared(42)
    除字符串字面量之外的字面量,如 42、true

  • 变量是左值

左右值区别

  • 左值持久,右值短暂
    左值有持久的状态,而右值要么是字面常量,要么是在表达式求值过程中创建的临时对象。
    由于右值引用只能绑定到临时对象,所以使用右值引用的代码可以自由地接管所引用的对象的资源,即可以进行移动。

  • 移动右值,拷贝左值
    如果一个类既有移动构造函数,也有拷贝构造函数,编译器使用普通的函数匹配规则来确定使用哪个构造函数,赋值操作的情况类似。

StrVec v1, v2;
v1 = v2; // 拷贝赋值,因为v2是左值
v1 = std::move(v2); // 强制变成右值了,使用移动赋值

StrVec getVec();
v2 = getVec();  // 移动赋值,因为getVec()是右值
  • 把 std::move(ptr1) 看作是一个有名字的右值。为了跟无名的纯右值 prvalue 相区别,C++ 里目前就把这种表达式叫做 xvalue。
    跟左值 lvalue 不同,xvalue 仍然是不能取地址的——这点上,xvalue 和 prvalue 相同。所以,xvalue 和 prvalue 都被归为右值 rvalue。
shared_ptr<shape> ptr1{new circle()}; // new circle() 就是一个纯右值
shared_ptr<shape> ptr2 = std::move(ptr1); // 可以把 std::move(ptr1) 看作是一个有名字的右值。为了跟无名的纯右值 prvalue 相区别,C++ 里目前就把这种表达式叫做 xvalue。

左值引用lvalue reference

当我们使用术语引用reference时,指的其实是左值引用(lvalue reference)。

  • 引用即别名,引用并非对象,它是为一个已经存在的对象所起的另一个名字。定义了一个引用之后,对其进行的所有操作都是在与之绑定的对象上进行的。

  • 引用必须初始化,一旦初始化完成,引用将和它的初始值对象一直绑定在一起:
    引用在初始化后,不可以改变;
    引用类型必须和绑定的对象的类型一致,但常量引用会丢失精度;
    引用类型的初始值必须是一个变量;
    常量引用可以引用常量;

  • 引用只能绑定在对象上,而不能与字面值或某个表达式的计算结果绑定在一起;

    int c = 2;
    ra = c; // 赋值操作,把c赋值给b,即赋值给a
    // &ra = c; // 错误,引用在初始化后,不可以改变

// Non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'
    const int e = 10;
// int &re = e; // 错误,引用类型的初始值必须是一个对象
    const int &re = e;
//    int &re1 = e * 42; // 错误,引用只能绑定在对象上,而不能与字面值或某个表达式的计算结果绑定在一起,a * 42是一个右值
    const int &re1 = e * 42; // 正确,可以将一个const引用绑定到一个右值上

    double d = 10.0F;
// Non-const lvalue reference to type 'int' cannot bind to a value of unrelated type 'double'
// int &rd = d; // 错误,引用类型必须和绑定的对象的类型一致,添加const后可以
    double &rd = d;
    const int &rd1 = d; // 正确,Clang-Tidy: Narrowing conversion from 'double' to 'int'

右值引用rvalue reference

为了支持移动操作,新标准引入了一种新的引用类型——右值引用。

  • 所谓右值引用就是必须绑定到右值的引用,我们通过&&而不是&来获得右值引用。

  • 右值引用有一个重要的性质:只能绑定到一个将要销毁的对象。
    因此,我们可以自由地将一个右值引用的资源“移动”到另一个对象中。

  • 一个右值引用也不过是某个对象的另一个名字而已。

  • 对于左值引用,我们不能将其绑定到要求转换的表达式、字面常量或是返回右值的表达式。
    右值引用有着完全相反的绑定特性:我们可以将一个右值引用绑定到这类表达式上,但不能将一个右值引用直接绑定到一个左值上。

  • 返回左值引用的函数,连同赋值、下标、解引用和前置递增/递减运算符,都是返回左值的表达式的例子。我们可以将一个左值引用绑定到这类表达式的结果上。
    返回非引用类型的函数,连同算术、关系、位以及后置递增/递减运算符,都生成右值。我们不能将一个左值引用绑定到这类表达式上,但我们可以将一个const的左值引用或者一个右值引用绑定到这类表达式上。

std::move

虽然不能将一个右值引用直接绑定到一个左值上,但我们通过std::move函数可以显式地将一个左值转换为对应的右值引用类型。

  • move调用告诉编译器:我们有一个左值,但我们希望像一个右值一样处理它。

  • 调用move就意味着承诺:
    除了对移后源对象进行赋值或销毁它外,我们将不再使用它。
    在调用move之后,我们不能对移后源对象的值做任何假设。
    我们可以销毁一个移后源对象,也可以赋予它新值,但不能使用一个移后源对象的值。

  • 不要随意使用移动操作
    通过在类代码中小心地使用move,可以大幅度提升性能。
    但由于一个移后源对象具有不确定的状态,对其调用std::move是危险的。当我们调用move时,必须绝对确认移后源对象没有其他用户。
    而如果随意在普通用户代码(与类实现代码相对)中使用移动操作,很可能导致莫名其妙的、难以查找的错误,而难以提升应用程序性能。

示例

    int a = 1;
    int &ra = a; // 引用必须要初始化
//    int &ra2 = 1; // 错误,引用只能绑定在对象上,而不能与字面值或某个表达式的计算结果绑定在一起
//    int &&rra = a; // 错误,不能将一个右值引用绑定到左值上
//    int &ra3 = a * 42; // 错误,引用只能绑定在对象上,而不能与字面值或某个表达式的计算结果绑定在一起,a * 42是一个右值
    const int &ra3 = a * 42; // 正确,可以将一个const引用绑定到一个右值上
    int &&rra2 = a * 42; // 正确,将rra2绑定到一个右值上
    int &&rra2 = a * 42; // 正确,将rra2绑定到一个右值上

//    int &&rrra = rra2; // 错误,表达式rra2是左值,变量是左值
    int &&rrra = std::move(rra2); // 正确,使用std::move将左值显式的变为右值

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/673741.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用busybox快速创建rootfs系统(硬件:atk-dl6y2c)

目录 概述 1 编译busybox 1.1 配置Makefile 1.2 需改参数 1.3 配置busybox 1.4 编译busybox 2 完善 rootfs下文件 2.1 rootfs 的“/lib”目录添加库文件 2.2 rootfs 的“usr/lib”目录添加库文件 2.3 创建其他目录 3 完善其他文件 3.1 完善etc/init.d/rcS 3.2 完善…

11.4 插入排序

目录 11.4 插入排序 11.4.1 算法流程 11.4.2 算法特性 11.4.3 插入排序的优势 11.4 插入排序 插入排序&#xff08;insertion sort&#xff09;是一种简单的排序算法&#xff0c;它的工作原理与手动整理一副牌的过程非常相似。 具体来说&#xff0c;我们在未排…

片上电控系统集成技术

一、背景 片上电机控制系统集成技术&#xff08;On-Chip Motor Control System Integration&#xff09;是一种先进的电子工程技术&#xff0c;它主要聚焦于将复杂的电机控制算法和硬件组件整合到单一集成电路&#xff08;IC&#xff09;中&#xff0c;以便于高效、精确地管理…

C基础-标准库下

上:http://t.csdnimg.cn/qj5uA 目录 七. math.h 八. setjmp.h 九. signal.h 十. stdarg.h 十一.stddef.h 十二. stdio.h 十三. stdlib. 十四. string.h 十五. time.h 七. math.h 定义了各种数学函数和一个宏。 宏和函数描述 序号宏 & 描述1HUGE_VAL 当函数的结…

C++11 lambda表达式和包装器

C11 lambda表达式和包装器 一.lambda表达式1.lambda表达式的引入2.基本语法和使用1.基本语法2.使用1.传值捕捉的错误之处2.传引用捕捉 3.lambda表达式的底层原理4.lambda的特殊之处5.lambda配合decltype的新玩法 二.function包装器1.概念2.包装函数1.包装普通函数2.包装成员函数…

【Oracle篇】rman全库异机恢复:从RAC环境到单机测试环境的转移(第四篇,总共八篇)

&#x1f4ab;《博主介绍》&#xff1a;✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ &#x1f4ab;《擅长领域》&#xff1a;✌️擅长Oracle、MySQL、SQLserver、阿里云AnalyticDB for MySQL(分布式数据仓库)、Linux&#xff0c;也在扩展大数据方向的知识面✌️…

odoo10 编写审批拒绝弹窗

前言 在日常中有很多审批场景&#xff0c;例如请销假。审批拒绝的时候应该给出原因&#xff0c;此时&#xff0c;在form界面点击拒绝的时候应该弹出输入窗口。如下图所示。 编写模型 模块的目录下&#xff0c;新建wizard文件夹&#xff0c;然后直接创建一个models.py和views.p…

idea实用快捷键(持续更新...)

文章目录 1、快速输入try/catch/finally2、选中多个光标3、实现接口4、方法参数提示5、查看某个类的子类6、弹出显示查找内容的搜索框 1、快速输入try/catch/finally CtrlAltT 2、选中多个光标 ShiftAlt单机多选 End可以全部到行尾&#xff0c;Home则可以全部回到行首 3、实现接…

MySQL 使用方法以及教程

一、引言 MySQL是一个流行的开源关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;广泛应用于Web开发、数据分析等领域。它提供了高效、稳定的数据存储和查询功能。同时&#xff0c;Python作为一种强大的编程语言&#xff0c;也提供了多种与MySQL交互的库&#…

中国人工智能区域竞争力研究报告(2024)

来源&#xff1a;赛迪顾问 近期历史回顾&#xff1a;2024年NoETL开启自动化数据管理新时代白皮书.pdf 创新引领用户“换新生活”-从AWE2024看家电及消费电子行业发展趋势报告&#xff08;精简版&#xff09;.pdf 2024智能网联汽车“车路云一体化”规模建设与应用参考指南&#…

字节裁员!开启裁员新模式。。

最近&#xff0c;互联网圈不太平&#xff0c;裁员消息此起彼伏。而一向以“狼性文化”著称的字节跳动&#xff0c;却玩起了“低调裁员”&#xff0c;用一种近乎“温柔”的方式&#xff0c;慢慢挤掉“冗余”的员工。 “细水长流”&#xff1a;裁员新模式&#xff1f; 不同于以往…

FreeRTOS基础(九):FreeRTOS的列表和列表项

今天我们将探讨FreeRTOS中的一个核心概念——列表&#xff08;List&#xff09;和列表项&#xff08;List Item&#xff09;。在实时操作系统&#xff08;RTOS&#xff09;中&#xff0c;任务的管理和调度是至关重要的&#xff0c;而FreeRTOS使用列表来实现这一功能。列表可以说…

城市低空经济“链接力”指数报告(2024)

来源&#xff1a;城市进化论&火石创造 近期历史回顾&#xff1a;2024年NoETL开启自动化数据管理新时代白皮书.pdf 创新引领用户“换新生活”-从AWE2024看家电及消费电子行业发展趋势报告&#xff08;精简版&#xff09;.pdf 2024智能网联汽车“车路云一体化”规模建设与应用…

鬼刀画风扁平化粒子炫动引导页美化版

源码介绍 分享一款引导页,响应式布局&#xff0c;支持移动PC 添加背景图片&#xff0c;美化高斯模糊 &#xff0c;删除蒙版人物部分&#xff0c;更图片人物画风更美好 删除雪花特效 替换字体颜色 添加底备案号 预留友情连接 效果预览 源码下载 https://www.qqmu.com/3381.h…

总结2024/6/3

省流&#xff0c;蓝桥杯国优&#xff0c;还是太菜了&#xff0c;听说都是板子题但是还是写不出来&#xff0c;靠暴力好歹没有爆0&#xff0c;还是得多练&#xff0c;明年加油了

分享5款.NET开源免费的Redis客户端组件库

前言 今天大姚给大家分享5款.NET开源、免费的Redis客户端组件库&#xff0c;希望可以帮助到有需要的同学。 StackExchange.Redis StackExchange.Redis是一个基于.NET的高性能Redis客户端&#xff0c;提供了完整的Redis数据库功能支持&#xff0c;并且具有多节点支持、异步编…

Python中的元素相乘与矩阵相乘(附Demo)

目录 前言1. 元素相乘2. 矩阵相乘3. 差异 前言 深度学习的矩阵相乘引发的Bug&#xff0c;由此深刻学习这方面的相关知识 在Python中&#xff0c;特别是使用NumPy库时&#xff0c;元素相乘和矩阵相乘是处理数组和矩阵时的常见操作 1. 元素相乘 元素相乘是指对两个相同形状的…

Windows端口本地转发

参考 微软Netsh interface portproxy 命令 界面端口代理的 Netsh 命令 | Microsoft Learn 使用Windows系统的portproxy功能配置端口转发 使用Windows系统的portproxy功能配置端口转发-阿里云帮助中心 (aliyun.com) 将来自0.0.0.0地址对端口35623的访问转发到172.18.106.16…

Python中degrees怎么用

degrees() 函数可以将弧度转换为角度。 语法 以下是 degrees() 方法的语法&#xff1a; import math math.degrees(x) 注意&#xff1a;degrees() 是不能直接访问的&#xff0c;需要导入 math 模块&#xff0c;然后通过 math 静态对象调用该方法。 参数 x -- 一个数值。 返…

苹果设备mac/Paid/phone 下载使用anki记忆卡

安卓的设备直接可以下 如果你这个&#xff0c;如图。 首先点击下列网址&#xff0c;下载&#xff0c;在里面搜索anki记忆卡 https://www.i4.cn 下载好&#xff0c;打开应用软件爱思助手。搜索anki记忆卡&#xff0c;下载&#xff0c;然后用数据线一端连接电脑一端连接手机或者…