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的意思是说明这个物体是盛饭的桶。这个例子说明,要理解一个单词的意思(例如“饭桶”)是要结合句中其他单词,结合整个句子,甚至要结合前后句理解。
而在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++标准中值类别的规则
举例来理解的话,对于xvalue表达式,有这样的规则:
如果一个表达式是函数调用或重载运算符表达式,且其返回类型为右值引用,例如 std::move(x),那么这个表达式是xvalue表达式
对于这个规则,我们可以这么理解。首先返回一个对象,肯定是要在栈上面预留内存空间的,所以这个对象是由identity的。返回类型是右值引用,所以它会让使用这个表达式的语句呈现移动语义,所以是可以动的。因此,这个表达式是xvalue表达式。
对于xvalue还有这样的规则
对象成员表达式,即"a.m",如果
a是右值且m是非引用类型的非静态数据成员,则这个表达式是xvalue表达式
这条规则可以这么理解,a是右值,也就是可以移动,那么对于a对象的一部分,m也应当是可以移动的。访问对象的“.”运算符实际上是指针的位移运算,既然要用指针,那么肯定是有地址的。因此,这个表达式是xvalue表达式。
再比如:
对象成员表达式,即"a.m",如果
m是成员枚举符或非静态成员函数,则这个表达式是prvalue表达式
不管是静态枚举符实际上是一个数字,成员函数实际上是指向代码段的地址,实际上也是一个数字,而且都是在编译时期就决定了的数字。cpu对这些数字操作时,这些数字是直接放在指令内部的,或者是放在寄存器中,而不会在内存中存在,所以他们是没有identity的。所以这个表达式是prvalue表达式。
C++标准还定义了很多规则,详细规定了哪些表达式是prvalue,哪些是xvalue,哪些又是lvalue。这些规则都可以用类似的方法分析并理解,而不需要去死记硬背。
C++11的value category(值类别)以及move semantics(移动语义)的更多相关文章
- C++11中的右值引用及move语义编程
C++0x中加入了右值引用,和move函数.右值引用出现之前我们只能用const引用来关联临时对象(右值)(造孽的VS可以用非const引用关联临时对象,请忽略VS),所以我们不能修临时对象的内容,右 ...
- C++11之 Move semantics(移动语义)(转)
转https://blog.csdn.net/wangshubo1989/article/details/49748703 按值传递的意义是什么? 当一个函数的参数按值传递时,这就会进行拷贝.当然,编 ...
- C++11新特性之 Move semantics(移动语义)
https://blog.csdn.net/wangshubo1989/article/details/49748703 这篇讲到了vector的push_back的两种重载版本,左值版本和右值版本.
- [置顶] Objective-C,/,ios,/iphone开发基础:分类(category,又称类别)
在c++中我们可以多继承来实现代码复用和封装使程序更加简练.在objective-c中只能单继承,不能多继承,那么除了协议protocol之外,我们可以实现类似多继承的一个方法就是,分类(catego ...
- extension(类扩展)和 category(类别)
extension(类扩展) 简单来说,extension在.m文件中添加,所以其权限为private,所以只能拿到源码的类添加extension.另外extension是编译时决议,和interfa ...
- C++ 11 中的右值引用
C++ 11 中的右值引用 右值引用的功能 首先,我并不介绍什么是右值引用,而是以一个例子里来介绍一下右值引用的功能: #include <iostream> #include &l ...
- [转载] C++11中的右值引用
C++11中的右值引用 May 18, 2015 移动构造函数 C++98中的左值和右值 C++11右值引用和移动语义 强制移动语义std::move() 右值引用和右值的关系 完美转发 引用折叠推导 ...
- C++ 11中的右值引用以及std::move
看了很多篇文章,现在终于搞懂了C++ 中的右值以及std::move 左值和右值最重要的区别就是右值其实是一个临时的变量 在C++ 11中,也为右值引用增加了新语法,即&& 比 ...
- C++11中的右值引用
原文出处:http://kuring.me/post/cpp11_right_reference May 18, 2015 移动构造函数 C++98中的左值和右值 C++11右值引用和移动语义 强制移 ...
随机推荐
- Pla
Pla(jdoj1006) 题目大意:给你n个矩形,并排放在一起,你的目的是将所有的矩形全部染色.你每次染的形状为一个矩形,问:最少需要染多少次? 注释:n<=10^6,wi , hi<= ...
- selenium2自动化测试学习笔记(二)
chromedriver报错问题解决了,真是无语 是因为chromedriver与浏览器版本不一致 http://chromedriver.storage.googleapis.com/index.h ...
- Docker深入浅出系列教程——Docker初体验
我是张飞洪,钻进浩瀚代码,十年有余,人不堪其累,吾不改其乐.我喜欢把玩代码,琢磨词句!代码算法让我穿透规律,文章摘句让我洞察人情.如果你觉得和我的看法不一样,请关注我的头条号,那我们一定合得来. Do ...
- 网络1712--c语言一二维数组作业总结
1.成绩摆前头 1.1基本要求(1分) 按时交 - 有分 未交 - 0分 迟交一周以上 - 倒扣本次作业分数 抄袭 - 0分 泛泛而谈(最多七分) 1.2评分要点 PTA作业总结(4分) 同学代码互评 ...
- 关于tomcat部署应用的三种方式
关于tomcat部署应用虽然不是一个经常的操作,因为一旦选择了一种部署方式,我们其他的应用就会不经大脑的使用这种既定模式, 如果不使用这种部署方式,但是对于其他的部署方式不是很清楚的话,很容易抓瞎,所 ...
- Vim 游戏 2048
给大家介绍一款可以在Vim里面玩的游戏 vim2048. 界面如图: 操作非常简单,可以用 hjkl 或者 上下左右方向键移动 项目开源地址为: https://github.com/wsdjeg/v ...
- Visual Studio 开发工具常用的插件
转载自落日故乡 http://www.spersky.com/post/vsPlugins.html 该博客中收集整理归纳了若干个常用的vs插件,比如高亮显示当前选择,垂直辅助线,折叠代码等等,具体 ...
- vue初尝试--项目结构
新建一个项目之后,我们来看一下项目的目录结构 几个主要文件的内容 index.html文件(入口文件,系统进入之后先进入index.html) <!DOCTYPE html> <ht ...
- Python爬虫基本原理
爬虫基本原理 1. 什么是爬虫 请求网站并提取数据的自动化程序. 2. 爬虫基本流程 发起请求 通过HTTP库向目标站点发起请求,即发送一个Request,请求可以包含额外的headers等信息,等待 ...
- "双非"应届生校招如何获得大厂青睐?(内附技术岗超全求职攻略)
写在前面的话 笔者从17年的2月份开始准备春招,其中遇到不少坑,也意识到自己走过的弯路.故写了这篇文章总结一番,本文适合主动学习的,对自己要学的课程不明确的,对面试有恐惧症的...等将来打算从事技术岗 ...