当前位置:首页 > 公众号精选 > C语言与CPP编程
[导读]来源:georgeguo C++98 中表达式值的类型只有左值和右值两种类型,可以取到地址的表达式就是左值,不是左值的值就是右值,而C++11中将表达式的值类型划分成了lvalue(左值)、rvalue(右值)、prvalue(纯右值)、xvalue(将亡值)、gvalue(泛左值) 5种。下

来源:georgeguo


C++98 中表达式值的类型只有左值和右值两种类型,可以取到地址的表达式就是左值,不是左值的值就是右值,而C++11中将表达式的值类型划分成了lvalue(左值)、rvalue(右值)、prvalue(纯右值)、xvalue(将亡值)、gvalue(泛左值) 5种。下文从基本概念并结合实际的例子区分C++11中的表达式值类型。

基本概念

表达式


  • 表达式的定义
    定义: 由运算符(operator)和运算对象(operand)构成的计算式(类似于数学上的算术表达式)。
    举例: 字面值(literal)和变量(variable)是最简单的表达式,函数的返回值也被认为是表达式。

值类别


  • 表达式是可求值的,对表达式求值将得到一个结果(result)。这个结果有两个属性:类型和值类别(value categories)。

  • 在c++11以后,表达式按值类别分,必然属于以下三者之一:左值(left value,lvalue),将亡值(expiring value,xvalue),纯右值(pure rvalue,pralue)。其中,左值和将亡值合称泛左值(generalized lvalue,glvalue),纯右值和将亡值合称右值(right value,rvalue)。


  • C++11中右值rvalue的概念包括两个部分:

1、将亡值(xvalue. expiring value),xvalue是C++11新增的概念,与右值引用相关的表达式,如: 将要被移动的对象、T&&函数返回值、std::move返回值和转换为 T&&的类型的转换函数的返回值等;
2、纯右值(pvalue, pure right value),如:非引用返回的临时变量、运算表达式产生的临时变量、原始字面量和lambda表达式等属于pvalue;

注意:“左值”是表达式的结果的一种属性,但更为普遍地,通常用“左值”来指代左值表达式。所谓左值表达式,就是指求值结果的值类别为左值的表达式。在后文中,我们依然用左值指代左值表达式。对于纯右值和将亡值,亦然。

左值、纯右值和将亡值的描述

左值


  • 描述:能够用&取地址的表达式是左值表达式。

一个区分左值和右值的便捷方法:看能不能对表达式取地址,若能,则为左值,若不能则为右值。所有的具名变量都是左值,而右值是不具名的。

举例:


  • 函数名和变量名

  • 返回左值引用的函数调用

  • 前置自增/自减运算符连接的表达式++i/--i

  • 由赋值运算符或复合赋值运算符连接的表达式(a=b、a+=b、a%=b)

  • 解引用表达式*p

  • 字符串字面值"abc"


纯右值


描述:满足下列条件之一的:


  • 1)本身就是赤裸裸的、纯粹的字面值,如3、false;

  • 2)求值结果相当于字面值或是一个不具名的临时对象。


举例:


  • 除字符串字面值以外的字面值

  • 返回非引用类型的函数调用

  • 后置自增/自减运算符连接的表达式i++/i--

  • 算术表达式(a+b、a&b、a<

  • 逻辑表达式(a&&b、a||b、~a)

  • 比较表达式(a==b、a>=b、a

  • 取地址表达式(&a)


将亡值


描述: 在C++11之前的右值和C++11中的纯右值是等价的。C++11中的将亡值是随着右值引用的引入而新引入的。换言之,“将亡值”概念的产生,是由右值引用的产生而引起的,将亡值与右值引用息息相关。所谓的将亡值表达式,就是下列表达式:


  • 1)返回右值引用的函数的调用表达式

  • 2)转换为右值引用的转换函数的调用表达

在C++11中,用左值去初始化一个对象或为一个已有对象赋值时,会调用拷贝构造函数或拷贝赋值运算符来拷贝资源,而当用一个右值(包括纯右值和将亡值)来初始化或赋值时,会调用移动构造函数或移动赋值运算符来移动资源,从而避免拷贝,提高效率。当该右值完成初始化或赋值的任务时,它的资源已经移动给了被初始化者或被赋值者,同时该右值也将会马上被销毁(析构)。也就是说,当一个右值准备完成初始化或赋值任务时,它已经“将亡”了。这种右值常用来完成移动构造或移动赋值的特殊任务,扮演着“将亡”的角色,所以C++11给这类右值起了一个新的名字——将亡值。

  • 举例:std::move()、tsatic_cast(x)(X是自定义的类,x是类对象,这两个函数常用来将左值强制转换成右值,从而使拷贝变成移动,提高效率。

C++11中值的类型总结

  • gvalue: has identity

  • lvalue:  has identity and can not be moved from

  • rvalue: can be moved from

  • xvalue: has identity and can be moved from

  • prvalue: does not have identity and can be moved from

对比说明

情况1:++i是左值,而i++是右值


  • ++i对i加1后再赋给i,最终的返回值就是i,所以,++i的结果是具名的,名字就是i;

  • 而对于i++而言,是先对i进行一次拷贝,将得到的副本作为返回结果,然后再对i加1,由于i++的结果是对i加1前i的一份拷贝,所以它是不具名的。假设自增前i的值是6,那么,++i得到的结果是7,这个7有个名字,就是i;而i++得到的结果是6,这个6是i加1前的一个副本,它没有名字,i不是它的名字,i的值此时也是7。

  • 可见,++i和i++都达到了使i加1的目的,但两个表达式的结果不同。


情况2:解引用表达式*p是左值,取地址表达式&a是纯右值


  • &(p)一定是正确的,因为p得到的是p指向的实体,&(p)得到的就是这一实体的地址,正是p的值。由于&(p)的正确,所以*p是左值。

  • 而对&a而言,得到的是a的地址,相当于unsigned int型的字面值,所以是纯右值。


情况3:a+b、a&&b、a==b都是纯右值


  • a+b得到的是不具名的临时对象,而a&&b和a==b的结果非true即false,相当于字面值。


情况4:字符串字面值是左值,而非字符串的字面量是纯右值


  • 不是所有的字面值都是纯右值,字符串字面值是唯一例外。

  • 早期C++将字符串字面值实现为char型数组,实实在在地为每个字符都分配了空间并且允许程序员对其进行操作,所以类似


cout<<&("abc")<<endl; char *p_char="abc";//注意不是char *p_char=&("abc");


情况5:具名的右值引用是左值,不具名的右值引用是右值。


  • 如:


void foo(int&& t) { // t 被一个右值表达式初始化 // 但是,t 本身是一个左值 }

继续深入学习

C++11中的右值引用和移动语义https://www.jianshu.com/p/61ea80fcf943

参考

  • Value categorieshttps://en.cppreference.com/w/cpp/language/value_category

  • 话说C++中的左值、纯右值、将亡值http://www.cnblogs.com/zpcdbky/p/5275959.html

  • What are rvalues, lvalues, xvalues, glvalues, and prvalues?https://stackoverflow.com/questions/3601602/what-are-rvalues-lvalues-xvalues-glvalues-and-prvalues

  • Value categorieshttps://en.cppreference.com/w/cpp/language/value_category


免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

Java是一门面向对象的编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向...

关键字: Java C++

摘要:介绍了基于RFID的小区门禁管理系统,通过RS232串口连接RFID的读写器与PC机,给出了PC机在VisualC++6.0软件环境下,采用C++语言进行编写软件界面,并利用VC6.0的ADO访问技术来访问Acce...

关键字: RFID Access C++ RS232

摘 要:针对分布式孔径数字全息系统要求保密性高,数据量大等特点,文中设计了基于C++语言与OpenCV函数库的三孔径数字成像系统软件。软件首先对各孔径间装配导致的再现像缩放、旋转误差进行校正,然后进行孔径综合,并通过微软...

关键字: 分布式孔径 数字全息 C++ OpenCV MFC

嵌入式系统是指以应用为中心、以计算机技术为基础,软件硬件可裁剪、适应应用系统对功能、可靠性、成本、体积、功耗严格要求的专用计算机系统。

关键字: QT 嵌入式 C++

IOBE 已公布 2020 年 9 月的编程语言排行榜。 在本期榜单中指数没有明显的变化,值得注意的是,java占有率继续下降,从上个月的14.43%降到13.48%. 回到2003,编程语言C++是真正的赢家。它在20...

关键字: 编程语言 C++

提到C/C++语言很多初学者都觉得,学到中间就进行不下去了,但如果你最难啃的那几块硬骨头拿下,一切都会顺畅许多,而且C++诞生很久了,因此有大量可以免费阅读编程文档。近日,在Quora上发现一份免费的C ++资料列表,涉...

关键字: C++

昨天提供了关于C语言的思维导图: 一文带你学C语言,详细知识点思维导图! 被一些朋友威胁。 我太难了,这不,求生欲极强的我,还是被迫再次营业。 思维导图原文地址: https://www.csdn.net/gather_...

关键字: C++

— 1 — 要实现什么函数呢? 假设我们要实现一个会自动扩展的数组类,我们需要实现函数呢?先从下面 main 函数使用的功能,看看有什么函数是需要我们实现的。 输出结果: 0 1 2 3 40 1 2 100 4 要实现...

关键字: C++

— 1 — 需求 有时候希望赋值运算符两边的类型可以不匹配。 比如:把一个  int  类型变量赋值给一个 Complex(复数)对象,或把一个  char*  类型的字符串赋值给一个字符串对象,此时就需要重载 = 赋值...

关键字: 运算符 C++

— 1 — 运算符重载的需求 C++ 预定义的运算符,只能用于基本数据类型的运算:整型、实型、字符型、逻辑型等等,且不能用于对象的运算。但是我们有时候又很需要在对象之间能用运算符,那么这时我们就要重载运算符,使得运算符能...

关键字: 运算符 C++
关闭
关闭