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右值引用和移动语义 强制移 ...
随机推荐
- JavaScript(第六天)【函数】
函数是定义一次但却可以调用或执行任意多次的一段JS代码.函数有时会有参数,即函数被调用时指定了值的局部变量.函数常常使用这些参数来计算一个返回值,这个值也成为函数调用表达式的值. 一.函数声明 函 ...
- PTA的使用简介
PTA(Programming Teaching Assistant)是PAT(Programming Ability Test)的配套练习平台. 1.关于PAT PAT(Programming Ab ...
- Java作业-集合
1. 本周学习总结 2. 书面作业 1. ArrayList代码分析 1.1 解释ArrayList的contains源代码 public boolean contains(Object o) { r ...
- 每日冲刺报告--Day2
敏捷冲刺每日报告--Day2 情况简介 今天我们三个人在一起开了会,分析了我们面临的情况以及下一阶段的计划.一个重大的改进是,我们准备把之前用txt文件格式存储订阅列表改成了文件json格式. 任务进 ...
- python pip包管理
pip 是一个安装和管理 Python 包的工具 , 是 easy_install 的一个替换品.本文将详细说明 安装 pip 的方法和 使用 pip 的一些基本操作如安装.更新和卸载 python ...
- scrapy 爬取当当网产品分类
#spider部分import scrapy from Autopjt.items import AutopjtItem from scrapy.http import Request class A ...
- border 三角形 有边框的 东西南北的 气泡三角形
链接地址:http://www.cnblogs.com/blosaa/p/3823695.html
- crlf注入攻击
1.crlf 注入攻击. 原理:http数据包通过\r\n\r\n来分开http header何http body 实现:首先这种攻击发生在应用层,且发生在服务器返回给我们的http reponse没 ...
- Gson序列化对象如何忽略字段
Gson序列化对象如何忽略字段 Gson版本 2.8.2 梗概 用注解@Expose(serialize = false, deserialize = false)在类的成员上以告诉Gson 跳过本字 ...
- Mego(03) - ORM框架的新选择
前言 从之前的两遍文章可以看出ORM的现状. Mego(01) - NET中主流ORM框架性能对比 Mego(02) - NET主流ORM框架分析 首先我们先谈下一个我们希望的ORM框架是什么样子的: ...