| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> C++知识库 -> C++ 值类别(value category)循序渐进(一)值类别是什么 -> 正文阅读 |
|
[C++知识库]C++ 值类别(value category)循序渐进(一)值类别是什么 |
学习C++比较深入点的朋友们对下面这些名词肯定不陌生:左值右值、左值引用、右值引用、移动语义、完美转发、std::forward、std::move,每一个都算不上浅显易懂,但无论是平常工作还是面试都可能遇到,这也是C++进阶必须要掌握的内容,这些其实都是一类问题或者说知识点,也就是值类别(value category),有章法地整体学习这一块内容比每个概念单个看有效得多,这里就开一个新系列来聊聊C++的值类别。整个内容主要以C++ 11之后的modern C++为准,这一块在C++的历史上变化还挺大的。 一、值类别的定义和分类关系首先我们要明确一点,值类别是针对表达式的,所以我们先来看看C++的表达式的定义:由各种运算对象(operands)和运算符(operators )组成的表明一个计算的式子,比如a + b或a.method(1) + b这种,但这里想要额外强调的是,即便没有额外的运算符,"hello word"这种字面量以及单个变量名也属于表达式。 其实每一个C++表达式都有两个重要的特征:类型(type)和值类别(value category),前者我们都很熟悉,就是int、float、vector这种类型,而后者很多人都不太了解。C++ 11之后基本的值类别大体上可以分为我们常说的左值和右值两种,也就是比较宽泛的左值右值分类,我们应该在很多地方见到过一个简略的定义:能取地址的就是左值,否则就是右值,虽然不是很严谨,但也充分表明了二者最大的区别:左值是有身份的,而右值没有。左值右值这个名称的来源就是因为他们在赋值表达式位置,左值可以在左边,右值只能在右边。在标准的定义里我们所说的左值右值对应的是glvalue(“generalized” lvalue,泛左值)和rvalue(右值,从定义上来看其实也就是泛右值,不知道为啥不叫grvalue),之所以叫泛是因为包含了不同的种类,是广义上的混合值类型,而它们所包含的基础值类型有三类:prvalue(纯右值)、xvalue(eXpiring value,将亡值)和lvaue(左值)。整体上来看glvalue包含lvalue和xvalue,rvalue包含prvalue和xvalue。因此xvalue从广义上讲既是左值也是右值。 1.1 基础值类别定义我们先来看下三种基础值类型的具体定义:
以上定义为了便于理解有简化的成分,建议理解得差不多后仔细揣摩下cppreference上的详细说明。也可以先单独看下C++11版本的定义,比较明确,在下文的之类型发展历史里有。 2.1 混合值类别定义除了基本分类以外,还有2个混合概念,一是包含lvalue和xvalue的glvalue(generalized lvalue,广义左值),二是包括xvalue和prvalue的rvalue(右值,个人认为也可以叫泛右值,也就是广义右值),表达式的各种值类型的关系如下图所示: 二、基本值类别包含的表达式种类和属性下面我们来看下Mordern C++的标准里所详细定义的各种表达式所属的基本值类别以及各个值类别的属性,详细的定义特别的多,如果是刚开始接触大概扫一遍有个概念就行。 2.1 lvalue(左值)2.1.1 包含种类
最后两条看起来是不是有点奇怪,为什么函数的右值引用会是lvalue,可以看看stackoverflow上的这个讨论。 2.1.2 属性
2.2 prvalue(纯右值)2.2.1 包含种类
2.2.2 属性
2.3 xvalue(将亡值)2.3.1 包含种类
2.3.2 属性
三、混合值类别属性前面说到了混合类别是由基本类别构成的,他们包含的具体的表达式就是基础类别的合集,因此这里只列举两种混合类别的属性。 3.1 glvalue(泛左值)
3.1 rvalue(右值)
四、左值引用和右值引用上面的定义里提到了左值引用和右值引用,这里就来简单介绍下。如果只是说引用,用C++的程序员肯定不陌生,但大部分人对引用的认识应该停留在是变量的别名,类似指针,传引用属于传地址有利于提高性能等,并不会太深究。 首先来看下引用声明的的定义:声明一个具名变量作为引用,也就是已经存在的对象或者函数的别名,也就是用一个引用变量指代已有的东西。引用在声明的时候就必须绑定到对象,既然值类别有左值右值之分,也就是已经存在的对象有左右值之分,理所应当的,引用也应该有,于是在C++11之后引用也分为了左值引用和右值引用,关于引用声明和初始化的细节可以参照reference和reference_initialization。 这里想说的是,左值引用和右值引用是两种不同的引用类型,他们可以绑定到的具体对象是由对象的值类型决定的,按照直觉左值引用应该可以绑定到glvalue,右值引用可以绑定到rvalue,比如:
从定义上来看我们也能看到整体上来说是这样的,但是也有些例外,可以为我们的编程带来便利,比如上面的关于左右值的属性就讲到了,rvalue可以用来初始化const 左值引用。 五、一些特殊类别上面的定义覆盖了绝大部分的表达式,但还有一些边角情况需要单独定义,如下: 5.1 未决成员函数调用(Pending member function call)对于表达式 a.mf 和 p->mf,其中 mf 是非静态成员函数,以及表达式 a.*pmf 和 p->*pmf,其中 pmf 是成员函数指针,被归类为prvalue表达式,但它们除了作为函数调用运算符的左操作数(比如 (p->*pmf)(args))以外,不能用来初始化引用、作为函数实参等其他任何目的。 5.2 Void表达式返回 void 的函数调用表达式,cast到 void 的类型转换表达式,以及抛异常表达式(throw-expressions),被归类为纯右值表达式,但它们不能用来初始化引用或者作为函数实参。它们可以用在舍弃值的语境(discarded-value contexts,例如自成一行,作为逗号运算符的左操作数等)和返回 void 的函数中的 return 语句中。另外,throw 表达式可用作条件运算符 ?: 的第二个和第三个操作数。 5.3 位域(Bit fields)位域可以通俗地理解为可以缩减位数整型类数据成员,比如:
代表某个位域的表达式(例如 a.m,其中 a 是类型 struct A { int m: 3; } 的左值)是glvalue表达式:它可用作赋值运算符的左操作数,但它不能被取地址,并且非 const 的左值引用不能绑定于它。const 左值引用或右值引用可以从位域泛左值初始化,但这会创建位域的一个临时副本,而不会直接绑定到位域。 六、区分值类别的作用各种类型的值类别我们已经搞清楚了,那么C++定义这些值类别有什么意义呢?或者说能给我们的编码提供什么样的能力、我们在写程序的过程中会用到这些特性来做什么呢?答案很明确,那就是C++11之后引入的移动语义(Move semantics)。 七、编程语言值类别发展历史前面说了并不是C++ 11引入的值类别,甚至都不是C++首次提出的,下面我们就来看下值类别的发展史。 7.1 CPLCPL语言首次为表达式引入了值类别:所有 CPL 表达式都能以“右侧模式 (right-hand mode)”求值,但只有某些类型的表达式在“左侧模式 (left-hand mode)”有意义。在右侧模式中求值时,表达式被当做一条进行值的计算(右侧值,或右值)的规则。在左侧模式中求值时,表达式的效果是给出一个地址(左侧值,或左值)。“左”和“右”代表“赋值之左”和“赋值之右”。 7.2 CC 语言遵循相似的分类法,但赋值的作用不再重要:C语言的表达式被分为“左值 (lvalue) 表达式”和其他(函数和非对象值),其中“左值 (lvalue)”的含义为标识一个对象的表达式,即“定位器值 (locator value)”。 7.3 C++982011 年前的 C++ 遵循 C 模型,但恢复了对非左值表达式的“右值 (rvalue)”称呼,把函数归类为左值,并添加了引用能绑定到左值但只有 const 的引用能绑定到右值的规则。几种非左值的 C 表达式在 C++ 中成为了左值表达式。 7.4 C++11随着移动语义引入到 C++11 之中,值类别被重新进行了定义,以区别以下两种表达式的互相独立的性质:
C++11 中:
7.5 C++17C++17 中,某些场合强制要求进行复制消除,而这要求将纯右值表达式从被它们所初始化的临时对象中分离出来,这就是我们现有的体系。需要注意的是,与相比较于C++11 的方案,prvalue不再可被移动。 参考: |
|
C++知识库 最新文章 |
【C++】友元、嵌套类、异常、RTTI、类型转换 |
通讯录的思路与实现(C语言) |
C++PrimerPlus 第七章 函数-C++的编程模块( |
Problem C: 算法9-9~9-12:平衡二叉树的基本 |
MSVC C++ UTF-8编程 |
C++进阶 多态原理 |
简单string类c++实现 |
我的年度总结 |
【C语言】以深厚地基筑伟岸高楼-基础篇(六 |
c语言常见错误合集 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/23 10:15:31- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |