栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++11的value category(值类别)以及move semantics(移动语义)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++11的value category(值类别)以及move semantics(移动语义)

转载请保留以下声明
  作者:赵宗晟
  出处:http://www.cnblogs.com/zhao-zongsheng/p/value_categories_and_move_semantics.html

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();

  2. vec = another_vec;

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

这两个语句都是“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++标准还定义了很多这样的规则,都可以用类似的方法分析并理解,而不需要去死记硬背。

 


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/232852.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号