左值、右值与右值引用

【C++】右值引用(极详细版) 右值引用的两个价值;万能引用和完美转发 阅读详情

转自:http://book.2cto.com/201306/25366.html

在C语言中,我们常常会提起左值(lvalue)、右值(rvalue)这样的称呼。而在编译程序时,编译器有时也会在报出的错误信息中会包含左值、右值的说法。不过左值、右值通常不是通过一个严谨的定义而为人所知的,大多数时候左右值的定义与其判别方法是一体的。一个最为典型的判别方法就是,在赋值表达式中,出现在等号左边的就是“左值”,而在等号右边的,则称为“右值”。比如:

a = b + c;

在这个赋值表达式中,a就是一个左值,而b + c则是一个右值。这种识别左值、右值的方法在C++中依然有效。不过C++中还有一个被广泛认同的说法,那就是可以取地址的、有名字的就是左值,反之,不能取地址的、没有名字的就是右值。那么这个加法赋值表达式中,&a是允许的操作,但&(b + c)这样的操作则不会通过编译。因此a是一个左值,(b + c)是一个右值。

这些判别方法通常都非常有效。更为细致地,在C++11中,右值是由两个概念构成的,一个是将亡值(xvalue,eXpiring Value),另一个则是纯右值(prvalue,Pure Rvalue)。

其中纯右值就是C++98标准中右值的概念,讲的是用于辨识临时变量和一些不跟对象关联的值。比如非引用返回的函数返回的临时变量值(我们在前面多次提到了)就是一个纯右值。一些运算表达式,比如1 + 3产生的临时变量值,也是纯右值。而不跟对象关联的字面量值,比如:2、‘c’、true,也是纯右值。此外,类型转换函数的返回值、lambda表达式(见7.3节)等,也都是右值。

而将亡值则是C++11新增的跟右值引用相关的表达式,这样表达式通常是将要被移动的对象(移为他用),比如返回右值引用T&&的函数返回值、std::move的返回值(稍后解释),或者转换为T&&的类型转换函数的返回值(稍后解释)。而剩余的,可以标识函数、对象的值都属于左值。在C++11的程序中,所有的值必属于左值、将亡值、纯右值三者之一。

注意 事实上,之所以我们只知道一些关于左值、右值的判断而很少听到其真正的定义的一个原因就是—很难归纳。而且即使归纳了,也需要大量的解释。

在C++11中,右值引用就是对一个右值进行引用的类型。事实上,由于右值通常不具有名字,我们也只能通过引用的方式找到它的存在。通常情况下,我们只能是从右值表达式获得其引用。比如:
T && a = ReturnRvalue();

这个表达式中,假设ReturnRvalue返回一个右值,我们就声明了一个名为a的右值引用,其值等于ReturnRvalue函数返回的临时变量的值。

为了区别于C++98中的引用类型,我们称C++98中的引用为“左值引用”(lvalue reference)。右值引用和左值引用都是属于引用类型。无论是声明一个左值引用还是右值引用,都必须立即进行初始化。而其原因可以理解为是引用类型本身自己并不拥有所绑定对象的内存,只是该对象的一个别名。左值引用是具名变量值的别名,而右值引用则是不具名(匿名)变量的别名。

在上面的例子中,ReturnRvalue函数返回的右值在表达式语句结束后,其生命也就终结了(通常我们也称其具有表达式生命期),而通过右值引用的声明,该右值又“重获新生”,其生命期将与右值引用类型变量a的生命期一样。只要a还“活着”,该右值临时量将会一直“存活”下去。

所以相比于以下语句的声明方式:
T b = ReturnRvalue();

我们刚才的右值引用变量声明,就会少一次对象的析构及一次对象的构造。因为a是右值引用,直接绑定了ReturnRvalue()返回的临时量,而b只是由临时值构造而成的,而临时量在表达式结束后会析构因应就会多一次析构和构造的开销。

不过值得指出的是,能够声明右值引用a的前提是ReturnRvalue返回的是一个右值。通常情况下,右值引用是不能够绑定到任何的左值的。比如下面的表达式就是无法通过编译的。
int c;
int && d = c;

相对地,在C++98标准中就已经出现的左值引用是否可以绑定到右值(由右值进行初始化)呢?比如:
T & e = ReturnRvalue();
const T & f = ReturnRvalue();

这样的语句是否能够通过编译呢?这里的答案是:e的初始化会导致编译时错误,而f则不会。

出现这样的状况的原因是,在常量左值引用在C++98标准中开始就是个“万能”的引用类型。它可以接受非常量左值、常量左值、右值对其进行初始化。而且在使用右值对其初始化的时候,常量左值引用还可以像右值引用一样将右值的生命期延长。不过相比于右值引用所引用的右值,常量左值所引用的右值在它的“余生”中只能是只读的。相对地,非常量左值只能接受非常量左值对其进行初始化。

既然常量左值引用在C++98中就已经出现,读者可能会努力地搜索记忆,想找出在C++中使用常量左值绑定右值的情况。不过可能一切并不如愿。这是因为,在C++11之前,左值、右值对于程序员来说,一直呈透明状态。不知道什么是左值、右值,并不影响写出正确的C++代码。引用的是左值和右值通常也并不重要。不过事实上,在C++98通过左值引用来绑定一个右值的情况并不少见,比如:
const bool & judgement = true;

就是一个使用常量左值引用来绑定右值的例子。不过与如下声明相比较看起来似乎差别不大。
const bool judgement = true;

可能很多程序员都没有注意到其中的差别(从语法上讲,前者直接使用了右值并为其“续命”,而后者的右值在表达式结束后就销毁了)。

事实上,即使在C++98中,我们也常可以使用常量左值引用来减少临时对象的开销,如代码清单3-20所示。

代码清单3-20
#include <iostream>
using namespace std;

struct Copyable {
    Copyable() {}
    Copyable(const Copyable &o) {
        cout << "Copied" << endl;
    }
};

Copyable ReturnRvalue() { return Copyable(); }
void AcceptVal(Copyable) {}
void AcceptRef(const Copyable & ) {}

int main() {
    cout << "Pass by value: " << endl;
    AcceptVal(ReturnRvalue()); // 临时值被拷贝传入
    cout << "Pass by reference: " << endl;
    AcceptRef(ReturnRvalue()); // 临时值被作为引用传递
}
// 编译选项:g++ 3-3-5.cpp -fno-elide-constructors

在代码清单3-20中,我们声明了结构体Copyable,该结构体的唯一的作用就是在被拷贝构造的时候打印一句话:Copied。而两个函数,AcceptVal使用了值传递参数,而AcceptRef使用了引用传递。在以ReturnRvalue返回的右值为参数的时候,AcceptRef就可以直接使用产生的临时值(并延长其生命期),而AcceptVal则不能直接使用临时对象。

编译运行代码清单3-20,可以得到以下结果:
Pass by value:
Copied
Copied
Pass by reference:
Copied

可以看到,由于使用了左值引用,临时对象被直接作为函数的参数,而不需要从中拷贝一次。读者可以自行分析一下输出结果,这里就不赘述了。而在C++11中,同样地,如果在代码清单3-20中以右值引用为参数声明如下函数:
void AcceptRvalueRef(Copyable && ) {}

也同样可以减少临时变量拷贝的开销。进一步地,还可以在AcceptRvalueRef中修改该临时值(这个时候临时值由于被右值引用参数所引用,已经获得了函数时间的生命期)。不过修改一个临时值的意义通常不大,除非像3.3.2节一样使用移动语义。
就本例而言,如果我们这样实现函数:
void AcceptRvalueRef(Copyable && s) {
   Copyable news = std::move(s);
}

这里std::move的作用是强制一个左值成为右值(看起来很奇怪?这个我们会在下面一节中解释)。该函数就是使用右值来初始化Copyable变量news。当然,如同我们在上小节提到的,使用移动语义的前提是Copyable还需要添加一个以右值引用为参数的移动构造函数,比如:
Copyable(Copyable &&o) { /* 实现移动语义 */ }

这样一来,如果Copyable类的临时对象(即ReturnRvalue返回的临时值)中包含一些大块内存的指针,news就可以如同代码清单3-19一样将临时值中的内存“窃”为己用,从而从这个以右值引用参数的AcceptRvalueRef函数中获得最大的收益。事实上,右值引用的来由从来就跟移动语义紧紧相关。这是右值存在的一个最大的价值(另外一个价值是用于转发,我们会在后面的小节中看到)。

对于本例而言,很有趣的是,读者也可以思考一下:如果我们不声明移动构造函数,而只声明一个常量左值的构造函数会发生什么?如同我们刚才提到的,常量左值引用是个“万能”的引用类型,无论左值还是右值,常量还是非常量,一概能够绑定。那么如果Copyable没有移动构造函数,下列语句:
Copyable news = std::move(s);

将调用以常量左值引用为参数的拷贝构造函数。这是一种非常安全的设计—移动不成,至少还可以执行拷贝。因此,通常情况下,程序员会为声明了移动构造函数的类声明一个常量左值为参数的拷贝构造函数,以保证在移动构造不成时,可以使用拷贝构造(不过,我们也会在之后看到一些特殊用途的反例)。

为了语义的完整,C++11中还存在着常量右值引用,比如我们通过以下代码声明一个常量右值引用。
const T && crvalueref = ReturnRvalue();

但是,一来右值引用主要就是为了移动语义,而移动语义需要右值是可以被修改的,那么常量右值引用在移动语义中就没有用武之处;二来如果要引用右值且让右值不可以更改,常量左值引用往往就足够了。因此在现在的情况下,我们还没有看到常量右值引用有何用处。

表3-1中,我们列出了在C++11中各种引用类型可以引用的值的类型。值得注意的是,只要能够绑定右值的引用类型,都能够延长右值的生命期。

表3-1 C++11中引用类型及其可以引用的值类型
引用类型 可以引用的值类型 注  记
 非常量左值 常量左值 非常量右值 常量右值 
非常量左值引用 Y N N N 无
常量左值引用 Y Y Y Y 全能类型,可用于拷贝语义
非常量右值引用 N N Y N 用于移动语义、完美转发
常量右值引用 N N Y Y 暂无用途

有的时候,我们可能不知道一个类型是否是引用类型,以及是左值引用还是右值引用(这在模板中比较常见)。标准库在<type_traits>头文件中提供了3个模板类:is_rvalue_reference、is_lvalue_reference、is_reference,可供我们进行判断。比如:
cout << is_rvalue_reference<string &&>::value;

我们通过模板类的成员value就可以打印出stirng &&是否是一个右值引用了。配合第4章中的类型推导操作符decltype,我们甚至还可以对变量的类型进行判断。当读者搞不清楚引用类型的时候,不妨使用这样的小工具实验一下。

左值与右值-左值引用与右值引用 摘要:左值(lvalue)和右值(rvalue)是C/C++中的重要概念。左值代表可寻址的持久对象,如变量;右值代表临时数据或字面量。判断标准包括能否位于赋值号左侧或获取地址。左值引用(&)只能绑定左值,右值引用(&&)专门引用右值,而常量左值引用(const&)可以引用两者。右值引用支持移动语义和完美转发,是C++11的重要特性。这些概念对于理解内存管理和性能优化至关重要。 阅读详情

相关推荐

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

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

baiiu 1208

左值引用和右值引用

1.左值和右值 在C++11中可以取地址的、有名字的就是左值,反之,不能取地址的、没有名字的就是右值(将亡值或纯右值)。 举个例子,int a = b+c, a 就是左值,其有变量名为a,通过&a可以获取该变量的地址;表达式b+c、函数int func()的返回值是右值,在其被赋值给某一变量前,我们不能通过变量名找到它,&(b+c)这样的操作则不会通过编译。 左值是可以放在赋值号左边可以被赋值的值;左值必须要在内存中有实体; 右值当在赋值号右边取出值赋给其他变量的值;右值可以在内存也可以在CPU寄存

qq_41181857的博客 8897

[C++ 从入门到精通] 18.左值、右值,左值引用、右值引用、move

丶布布转载注明出处。

Loewen丶的小窝 1707

初识C++之左值引用与右值引用

本文介绍了C++中的左值引用和右值引用的概念、作用和使用场景使用方法

Masquerena114514的博客 3533

DVT Eclipse保姆级入门教程(一)

介绍 waokspace工作区 是Eclipse平台、已安装插件的存储、配置和临时信息的首选项 在工作区可以定义项目,但不需要将源文件导入或者复制到工作区中,可以在项目中指向任何文件夹或者文件; 路径都不要出现中文; 每次启动Eclipse时,都可以创建一个新的工作空间或指定一个现有的工作空间目录; 可以创建许多工作区,每个工作区专用于某种活动。 project项目 首次创建项目时,Eclipse平台会在你指定的目录中创建一个.project项目。 一些插件可能会在项目目录中创建其他信息,例如DVT创

SummerXRT的博客 1万+

麒麟V10系统命令实战指南:从入门到精通

本文为麒麟V10操作系统用户提供了一份从入门到精通的命令行实战指南。内容涵盖文件管理、进程监控、网络诊断、文本处理及系统管理等核心场景,旨在帮助用户转变思维,掌握高效解决实际问题的系统命令技巧,从而提升在麒麟V10环境下的运维与工作效率。

sat99的博客 597

左值引用与右值引用详解

左值是一个表示数据的,比如:变量名、解引用的指针变量。一般地,我们可以获取它的地址和对它赋值,定义时const修饰符后的左 值,不能给他赋值,但是可以取它的地址。总体而言,可以取地址的对象就是左值。

w8848w的博客 1650

手把手教你用Qt和DSP28335实现串口Bootloader,附赠源码和避坑指南

本文详细介绍了如何使用Qt和DSP28335实现串口Bootloader,包括开发环境搭建、通信协议设计、Qt上位机开发、DSP端Bootloader核心实现以及应用程序工程的特殊配置。通过实战案例和源码分享,帮助开发者快速掌握嵌入式固件升级技术,提升工业设备远程升级的效率和可靠性。

weixin_29176179的博客 137

详解 C++ 左值、右值、左值引用以及右值引用

主要讲解了 C++11 的右值引用(及其支持的移动语义和完美转发)。

Butayarou的博客 2万+

prokka-原核及病毒基因组高效便捷注释

文章目录简介安装使用参考 简介 Prokka: rapid prokaryotic genome annotation 全基因组注释是在一组基因组DNA序列中识别感兴趣的特征,并用有用的信息标记它们的过程。Prokka是一款软件工具,可以快速注释细菌、古生菌和病毒基因组,并生成符合标准的输出文件。 安装 conda install -c conda-forge -c bioconda -c defaults prokka Test Type prokka and it should output its

Neptuneyut的博客 3312

3DGS 2025实战指南:从安装到训练全流程解析

本文提供了3D Gaussian Splatting (3DGS) 从零开始的完整实战指南。详细解析了使用gsplat库进行环境配置、依赖安装(包括pycolmap和fused-ssim)、数据准备与COLMAP处理、核心训练流程及参数调优,并探讨了2025年3DGS在动态场景、可控编辑等领域的最新进展,帮助开发者快速掌握这一前沿3D重建技术。

b3n4m5q6w7的博客 359

左值、右值、左值引用和右值引用

文章目录左值和右值什么是左值和右值?举例说明左值引用和右值引用什么是左值引用和右值引用?左值和右值的转换右值变左值通过右值引用&&通过引用const &通过通用转换 forward左值和右值 什么是左值和右值? 按照我们常规理解左值就是在等号左边的值,右值是等号右边的值。如果你要这么理解,你就会发现遇到++i或i++这一类的语句你就开始混乱了! 其实左值和右值是我们习惯性的叫法,他的全拼应该是: 左值是:locator value(可寻址的数据) 右值是:read value(不可

四库全书的酷 5525

YOLO26改进策略【Neck】| arXiv 2025 分层多头注意力HMHA 子空间拆分提多样 + 通道重排降冗余,强化目标检测精度

HMHA(Hierarchical Multi-Head Attention,分层多头注意力)旨在解决传统多头注意力(MHA)在图像恢复任务中的核心缺陷——特征冗余。传统MHA将通道均匀拆分给各个头,导致不同头学习的子空间信息相似,进而出现多头关注同一区域、忽略退化区域修复的问题,限制了模型的特征表达能力。HMHA通过“分层子空间拆分+通道重排序”策略,让每个头学习差异化、互补的上下文特征,从根源上缓解冗余,提升图像恢复任务中对复杂退化场景的特征捕捉能力。

Limiiiing的博客 212

C++中左值(引用)及右值(引用)详解

写C++代码编译时,有时会出现左值问题错误或右值错误,那左值和右值究竟是什么呢??? 一、左值与右值 啥是左值和右值呢? 左值:在内存有确定存储地址、有变量名,表达式结束依然存在的值,简单来说左值就是非临时对象。 右值:就是在内存没有确定存储地址、没有变量名,表达式结束就会销毁的值,简单来说右值就是临时对象。 int a = 0; // 在这条语句中,a 是左值,0 是临时值,就是右值。 左值可以分为两类:非常量左值和常量左值; int a=10; /.

一包辣条包邮!的博客 9445

6-Halcon-- var_threshold 函数功能(用于基于局部统计特性的动态阈值分割的算子)

var_threshold 是 HALCON 中用于基于局部统计特性的动态阈值分割的算子,特别适用于处理低对比度图像或纹理均匀但包含细微差异的场景。与 dyn_threshold 不同,它不依赖预处理生成的参考图像,而是直接在局部区域内计算灰度值的统计特性(均值和标准差),从而自适应调整阈值。

weixin_45590420的博客 1329

DRV8711驱动器的各寄存器的介绍

drv8711驱动器

PCLintelligence的博客 1636

【C++ | 左值、右值】一文了解C++的左值、右值、左值引用(&)、右值引用(&&)

👉本文介绍了C++的左值、右值、左值引用、右值引用。左值一般表示某一块内存,可以获取其地址;右值则一般只表示数据,不能被获取地址,很多情况,左值的值可以是右值;左值引用是某个左值的别名;右值引用是某个右值的别名。

wkd_007的博客 2823

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

C++高级特性:重新理解左值、右值、左值引用与右值引用详解(四)

另外还有引用初始化的问题,引用只能在定义的时候初始化(定义的时候必须指定一个类型)。定义完毕无法初始化和修改指向,虽然无法修改指向但是可以修改值。b = y:实际上是将y的右值给了b当右值,不是把y的左值给了b当右值。int&b = x;b = y;/* 输出*/左值引用不是引用不了一个临时变量值吗,那右值引用来引用临时变量(东厂管不了的西厂管)右值引用却无法引用一个存在地址的左值(有实际地址)// 报错int t = 10;int& a = t;// 报错。

weixin_43808717的博客 5770

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

左值引用、右值引用详解

左值引用、右值引用 右值引用 1 右值引用概念 什么是左值?什么是左值引用? 什么是右值?什么是右值引用? 2左值与右值 总结 C++11对右值进行了严格的区分: 3 引用与右值引用比较 4 右值引用使用场景和意义(移动语义) 总结 5 右值引用引用左值及其一些更深入的使用场景分析 总结 6 完美转发 为什么右值引用引用了右值以后,在后面属性就会退化成左值?......

Liyuyue的博客 3146

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

左值引用 VS 右值引用 —— 基础篇

左值引用就是 对左值的引用,本质上就是 对左值取别名,我们可以通过这个别名来操作这个左值。int main()//对const修饰的变量的引用需要加const//避免权限放大return 0;左值引用不仅可以引用左值,还可以引用右值,但是左值引用 引用右值的时候需要加 const右值则与左值相反,它们表示的是临时的、匿名的值通常不具有持久的存储位置,如字面常量表达式返回值函数返回的临时变量(这个不能是左值引用返回)。return ret;

知其然 知其所以然 1973

C++中左值引用与右值引用的区别

1.左值和右值 首先,左值和右值是表达式的属性。C++中的表达式,不是左值就是右值。左值可以位于赋值语句的左侧,而右值则不能;可以做一个简单的归纳:当一个对象被用作右值的时候,用的是对象的值;当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。需要右值的地方可以用左值代替,但不能把右值当成左值使用。 介绍完左值右值,接下来介绍左值引用和右值引用。 2.左值引用 左值引用就是我们平常使用的“...

Kyson_的博客 9051

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

1、左值和右值 左值(left-values),缩写:lvalues ,located value 可定位值,其含义是可以明确其存放地址的值,更确切说对其的使用是基于地址 右值(right-values),缩写:rvalues , read value 可读的值,通常指代赋值运算=右侧的常量值,字面值,或者函数的返回值,它们没有具体的指代名,即无法通过地址访问,通常在赋值表达式结束后变销毁。 一般可以认为:左值对应变量的地址,右值对应变量的值,首先说左值和右值,他们绝不是简单的等号左边和右边的..

u014430031的博客 4114

左值、左值引用、右值、右值引用

关于左值 右值示例 可看这里 1、左值和右值的概念          左值是可以放在赋值号左边可以被赋值的值;左值必须要在内存中有实体;          右值当在赋值号右边取出值赋给其他变量的值;右值可以在内存也可以在CPU寄存器。          一个对象被用作右值时,使用的是它的内容(值),被当作左值时,使用的是它的地址。 2、引用         引用是C++语法做的优化,引用的...

学之知之的博客 3万+

左值、右值、左值引用、右值引用

1. 左值 左值(lvalue,left value),顾名思义就是赋值符号左边的值,可以取地址。准确来说,左值是表达式(不一定是赋值表达式)后依然存在的持久对象。 可以将左值看作是一个关联了名称的内存位置,允许程序的其他部分来访问它。在这里,我们将 "名称" 解释为任何可用于访问内存位置的表达式。所以,如果 arr 是一个数组,那么 arr[1] 和 *(arr+1) 都将被视为相同内存位置的“名称”。 2. 右值 右值(rvalue,right value),右边的值,是指表达式结束后就不再存在

yizhiniu_xuyw的专栏 2183

左值、右值、左值引用、右值引用及移动构造函数

左值是一个表示数据的表达式,它代表一个具名的内存位置,程序可以获取其地址,可以通过地址访问它们,是可被引用的数据对象。

Jaguar_95的博客 1239
上一篇: 成员函数,非成员函数和友元函数
下一篇: C++派生访问控制说明符
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值