C++11中value category(值类别)及move semantics(移动语义)的介绍
前言
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的意思是说明这个物体是盛饭的桶。这个例子说明,要理解一个单词的意思(例如“饭桶”)是要结合句中其他单词,以及整个句子的。
在C++语言中也是类似的。下面有两个“句子”(语句):
- vec = vector<int>();
- 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是怎么定义的?
其实所有表达式都有以下两种属性:
- 是否有identity(同一性,或者说“有身份”):是否可以与另一个表达式或对象比较,判断是否是同一个实体。比如,如果有地址,可以比较他们的地址相同;
- 是否可以移动:如果出现在赋值,初始化等语句中,是否会使语句呈现移动语义。
于是有:
- 有identity,也可以移动的表达式为xvalue表达式;
- 有identity,但不能移动的表达式为lvalue表达式;
- 没有identity,但是可以移动的表达式为prvalue表达式;
至于没有identity,也不可以移动的表达式,在实际应用中不存在这样的表达式,也没必要有这样的表达式。
对于另外两种值类别,我们可以这么总结:
- 有identity的表达式,值类别为glvalue;
- 可以移动的表达式,值类别为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++11中value category(值类别)及move semantics(移动语义)的介绍
本文地址:https://www.xiuzhanwang.com/a1/Cyuyan/785.html
您可能感兴趣的文章
- 04-02func函数+在C语言 func函数在c语言中
- 04-02c语言中对数函数的表达式 c语言中对数怎么表达
- 04-02c语言没有round函数 round c语言
- 04-02C语言中怎么打出三角函数 c语言中怎么打出三角函数的值
- 01-10深入理解C++中常见的关键字含义
- 01-10深入Main函数中的参数argc,argv的使用详解
- 01-10APUE笔记之:进程环境详解
- 01-10c++中inline的用法分析
- 01-10如何寻找数组中的第二大数
- 01-10C++大数模板(推荐)
阅读排行
本栏相关
- 04-02c语言函数调用后清空内存 c语言调用
- 04-02func函数+在C语言 func函数在c语言中
- 04-02c语言的正则匹配函数 c语言正则表达
- 04-02c语言用函数写分段 用c语言表示分段
- 04-02c语言中对数函数的表达式 c语言中对
- 04-02c语言编写函数冒泡排序 c语言冒泡排
- 04-02c语言没有round函数 round c语言
- 04-02c语言分段函数怎么求 用c语言求分段
- 04-02C语言中怎么打出三角函数 c语言中怎
- 04-02c语言调用函数求fibo C语言调用函数求
随机阅读
- 01-10使用C语言求解扑克牌的顺子及n个骰子
- 08-05DEDE织梦data目录下的sessions文件夹有什
- 01-11ajax实现页面的局部加载
- 08-05织梦dedecms什么时候用栏目交叉功能?
- 01-11Mac OSX 打开原生自带读写NTFS功能(图文
- 01-10SublimeText编译C开发环境设置
- 04-02jquery与jsp,用jquery
- 01-10C#中split用法实例总结
- 08-05dedecms(织梦)副栏目数量限制代码修改
- 01-10delphi制作wav文件的方法