C++语言的value category(值类别)以及move semantics(移动语义)
小标 2018-07-25 来源 : 阅读 1147 评论 0

摘要:本文主要向大家介绍了C++语言的value category(值类别)以及move semantics,通过具体的内容向大家展示,希望对大家学习C++语言有所帮助。

本文主要向大家介绍了C++语言的value category(值类别)以及move semantics,通过具体的内容向大家展示,希望对大家学习C++语言有所帮助。

C++11之前value categories只有两类,lvalue和rvalue,在C++11之后出现了新的value categories,即prvalue, glvalue, xvalue。不理解value categories可能会让我们遇到一些坑时不知怎么去修改,所以理解value categories对于写C++的人来说是比较重要的。而理解value categories离不开一个概念——move semantics。了解C++11的人我相信都了解了std::move,右值引用,移动构造/移动复制等概念,但是对move semantics这个概念的准确定义,可能还有很多人比较模糊。我想通过这篇文章谈一谈我对value categories和move semantics的理解。首先从move semantics开始。

什么是move semantics(移动语义)?

semantics是来自语言学的一个概念,翻译成中文就是“语义”。说到计算机语言,可能有很多人认为他是计算机科学下面的子门类。实际上他是计算机科学和语言学的交叉科目,里面有很多概念都来自语言学的内容,甚至也有语言学科班的学生之后去做编译的研究/工作。所以我们先从自然语言入手,通过类比能够更好地理解move semantics。下面有两个句子:

1. 他是饭桶。

2. 这是饭桶。

这两句话里面都有“饭桶”这个词,但是两个句子中“饭桶”意思却不一样。从语法上来看,这俩都是“<代词>是饭桶”的形式,只有代词不一样,但句子意思却完全不一样了。句子1的意思是骂一个人很没用,句子2的意思是说明这个物体是盛饭的桶。这个例子说明,要理解一个单词的意思(例如“饭桶”)是要结合句中其他单词,以及整个句子的。

在C++语言中也是类似的。下面有两个“句子”(语句):

1. vec = vector<int>();

2. vec = another_vec;

其中,vec和another_vec都是vector<int>类型的变量。

这两个语句都是“vec = XXXX;”的形式,但是语句1是把XXXX移动到变量vec,语句2是把XXXX拷贝给vec。两个语句中都有“=”运算符,但是语句1中的意思是“移动到”,语句2中的意思是“拷贝给”。所以“=”运算符和整个句子的意思是由XXXX的类型决定的。我们可以说语句1有移动的意思,语句2有拷贝的意思,或者说,语句1中的“=”是移动的意思,语句2中的“=”是拷贝的意思。更正式地说,语句1呈现了移动语义,语句2呈现了拷贝语义,语句1中的“=”呈现了移动语义,语句2中的“=”呈现了拷贝语义。用英文说则是,statement 1 displayed move semantics; statement 2 displayed copy semantics; operator= in statement 1 displayed move semantics; operator= in statement 2 displayed copy semantics。

其实“移动语义”翻译成白话就是“移动的意思”。

怎么理解5种value categories(值类别)?

C++中的每个表达式都有两种属性,一个是type(类型),另一个就是value category(值类别)。每个表达式的值类别一定属于且仅属于prvalue (pure rvalue), xvalue, lvalue三种中的一种。prvalue和xvalue统称为rvalue,xvalue和lvalue统称为glvalue (generalized lvalue),如下图所示:

 

那么,prvalue,xvalue和lvalue是怎么定义的?

其实所有表达式都有以下两种属性:

1. 是否有identity(同一性,或者说“有身份”):是否可以与另一个表达式或对象比较,判断是否是同一个实体。比如,如果有地址,可以比较他们的地址相同;

2. 是否可以移动:如果出现在赋值,初始化等语句中,是否会使语句呈现移动语义。

于是有:

1. 有identity,也可以移动的表达式为xvalue表达式;

2. 有identity,但不能移动的表达式为lvalue表达式;

3. 没有identity,但是可以移动的表达式为prvalue表达式;

至于没有identity,也不可以移动的表达式,在实际应用中不存在这样的表达式,也没必要有这样的表达式。

对于另外两种值类别,我们可以这么总结:

1. 有identity的表达式,值类别为glvalue;

2. 可以移动的表达式,值类别为rvalue。

分析理解C++标准中决定值类别的规则

C++标准给出了一系列规则,来规定哪些表达式有哪种值类别。我们可以结合上面给出的值类别定义去理解这些规则。举个例子,对于xvalue表达式,有这样的规则:

如果一个表达式是函数调用或重载运算符表达式,且其返回类型为右值引用,例如 std::move(x),那么这个表达式是xvalue表达式

对于这个规则,我们可以这么理解:首先,如果要返回一个对象,肯定是要在栈上面预留内存空间的,所以这个对象是有identity的。第二,返回类型是右值引用,所以它会让使用这个表达式的语句呈现移动语义,所以是可移动的。因此,这个表达式是xvalue表达式。

对于xvalue还有这样的规则

对象成员表达式,即"a.m",如果 a 是右值且 m 是非引用类型的非静态数据成员,则这个表达式是xvalue表达式

这条规则可以这么理解:首先,a是右值,也就是可以移动,那么作为a对象的一部分,m也应当是可以移动的。第二,访问对象的“.”运算符实际上是计算地址偏移,既然有地址,那么肯定是有identity的。因此,这个表达式是xvalue表达式。

再比如:

对象成员表达式,即"a.m",如果 m 是成员枚举符或非静态成员函数,则这个表达式是prvalue表达式

枚举符在编译后其实就是一个数字;成员函数在编译后实际上是指向代码段的地址,实际上也是一个数字。这两个数字都是在编译时期就决定了的数字。cpu使用这些数字时,这些数字是直接放在指令内部或者是放在寄存器中的,不会放在内存中,所以他们是没有identity的。其实换个角度想,因为他们只是一个值,不是变量,所以没有identity也是很合理的。因此,这个表达式是prvalue表达式。

C++标准还定义了很多这样的规则,都可以用类似的方法分析并理解,而不需要去死记硬背。

以上就介绍了C/C+的相关知识,希望对C/C+有兴趣的朋友有所帮助。了解更多内容,请关注职坐标编程语言C/C+频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程