转载请保留以下声明
  作者:赵宗晟
  出处: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<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++标准中值类别的规则

举例来理解的话,对于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(移动语义)的更多相关文章

  1. C++11中的右值引用及move语义编程

    C++0x中加入了右值引用,和move函数.右值引用出现之前我们只能用const引用来关联临时对象(右值)(造孽的VS可以用非const引用关联临时对象,请忽略VS),所以我们不能修临时对象的内容,右 ...

  2. C++11之 Move semantics(移动语义)(转)

    转https://blog.csdn.net/wangshubo1989/article/details/49748703 按值传递的意义是什么? 当一个函数的参数按值传递时,这就会进行拷贝.当然,编 ...

  3. C++11新特性之 Move semantics(移动语义)

    https://blog.csdn.net/wangshubo1989/article/details/49748703 这篇讲到了vector的push_back的两种重载版本,左值版本和右值版本.

  4. [置顶] Objective-C,/,ios,/iphone开发基础:分类(category,又称类别)

    在c++中我们可以多继承来实现代码复用和封装使程序更加简练.在objective-c中只能单继承,不能多继承,那么除了协议protocol之外,我们可以实现类似多继承的一个方法就是,分类(catego ...

  5. extension(类扩展)和 category(类别)

    extension(类扩展) 简单来说,extension在.m文件中添加,所以其权限为private,所以只能拿到源码的类添加extension.另外extension是编译时决议,和interfa ...

  6. C++ 11 中的右值引用

    C++ 11 中的右值引用 右值引用的功能 首先,我并不介绍什么是右值引用,而是以一个例子里来介绍一下右值引用的功能: #include <iostream>    #include &l ...

  7. [转载] C++11中的右值引用

    C++11中的右值引用 May 18, 2015 移动构造函数 C++98中的左值和右值 C++11右值引用和移动语义 强制移动语义std::move() 右值引用和右值的关系 完美转发 引用折叠推导 ...

  8. C++ 11中的右值引用以及std::move

    看了很多篇文章,现在终于搞懂了C++ 中的右值以及std::move   左值和右值最重要的区别就是右值其实是一个临时的变量 在C++ 11中,也为右值引用增加了新语法,即&&   比 ...

  9. C++11中的右值引用

    原文出处:http://kuring.me/post/cpp11_right_reference May 18, 2015 移动构造函数 C++98中的左值和右值 C++11右值引用和移动语义 强制移 ...

随机推荐

  1. 微信公众平台开发,模板消息,网页授权,微信JS-SDK,二维码生成(4)

    微信公众平台开发,模板消息,什么是模板消息,模板消息接口指的是向用户发送重要的服务通知,只能用于符合场景的要求中去,如信用卡刷卡通知,购物成功通知等等.不支持广告营销,打扰用户的消息,模板消息类有固定 ...

  2. CSS3美化网页元素

    <span>标签 </span>属性名 含义 举例font-family 设置字体类型 font-family:"隶书"font-size 设置字体大小 f ...

  3. MaxPooling的作用

    maxpooling主要有两大作用 1. invariance(不变性),这种不变性包括translation(平移),rotation(旋转),scale(尺度)2. 保留主要的特征同时减少参数(降 ...

  4. 大数据hadoop面试题2018年最新版(美团)

    还在用着以前的大数据Hadoop面试题去美团面试吗?互联网发展迅速的今天,如果不及时更新自己的技术库那如何才能在众多的竞争者中脱颖而出呢? 奉行着"吃喝玩乐全都有"和"美 ...

  5. 基于ECharts的饼状数据展示

    一.导入ECharts文件 二.HTML代码 大小后期自己调 三.后台代码 四.js代码 不要问为什么- -我是扒下来的 可复制代码: //基于准备好的dom,初始化echarts实例 var myD ...

  6. drbd(四):drbd多节点(drbd9)

    1.drbd多节点简介 在drbd9以前,drbd一直只能配置两个节点,要么是primary/secondary,要么是primary/primary.虽然在这些版本上也能配置第三个节点实现三路节点的 ...

  7. 第二次作业-关于Steam游戏平台的简单分析

    1.1 Steam平台的简单介绍 你选择的产品是? 如题,这次的作业我选择了Steam作为分析的对象. 为什么选择该产品作为分析? 我选择数字游戏贩售平台STEAM作为分析对象的原因有以下几点: 1. ...

  8. 2017 清北济南考前刷题Day 3 morning

    实际得分:100+0+0=100 T1 右上角是必败态,然后推下去 发现同行全是必胜态或全是必败态,不同行必胜必败交叉 列同行 所以n,m 只要有一个是偶数,先手必胜 #include<cstd ...

  9. C++高效安全的运行时动态类型转换

    关键字:static_cast,dynamic_cast,fast_dynamic_cast,VS 2015. OS:Window 10. C++类之间类型转换有:static_cast.dynami ...

  10. python基础学习篇章一

    一. 对Python的认识 1. Python的标准实现方式是将源代码的语句编译为字节码的形式,之后再将字节码解释出来.由于字节码是一种与平台无关的形式,字节码具有可移植性.但是Python没有将代码 ...