iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >详解C++中的左值,纯右值和将亡值
  • 523
分享到

详解C++中的左值,纯右值和将亡值

2024-04-02 19:04:59 523人浏览 安东尼
摘要

目录引入一.表达式二.值类别三.左值四.纯右值五.将亡值六.注意引入 c++中本身是存在左值,右值的概念,但是在C11中又出现了左值,纯右值,将亡值得概念;这里我们主要介绍这些值的概

引入

c++中本身是存在左值,右值的概念,但是在C11中又出现了左值,纯右值,将亡值得概念;这里我们主要介绍这些值的概念。

一.表达式

定义:由运算符和运算对象构成的计算式(类似数学中的算术表达式)

每个 C++ 表达式(带有操作数的操作符、字面量、变量名等)可按照两种独立的特性加以辨别:**类型和值类别 **(value cateGory)。每个表达式都具有某种非引用类型,且每个表达式只属于三种基本值类别中的一种:纯右值 (prvalue)、亡值 (xvalue)、左值 (lvalue)。

二.值类别

对于表达式来说:表达式是可以求值的,对表达式求值将得到一个结果,这个结果有两个属性:类型和值类别。

在C++11以后表达式按值类别分类,必然属于以下三者之一:

  • 左值
  • 将亡值
  • 纯右值

其中,左值和将亡值合称为泛左值,纯右值和将亡值合称为右值

三.左值

左值:能够用&取地址的表达式为左值表达式

下列表达式是左值表达式:

  1. 变量、函数、模板形参对象 (C++20 起)或数据成员的名字,不论类型,例如 std::cin 或 std::endl。即使变量的类型是右值引用,由它的名字构成的表达式仍是左值表达式;
  2. 返回类型是左值引用的函数调用或重载运算符表达式,例如 std::getline(std::cin, str)、std::cout << 1、str1 = str2 或 ++it;
  3. a = b,a += b,a %= b,以及所有其他内建的赋值及复合赋值表达式;
  4. ++a 和 --a,内建的前置自增与前置自减表达式;
  5. *p,内建的间接寻址表达式;
  6. a[n] 和 n[a],内建的下标表达式,当 a[n] 中的一个操作数是数组左值时 (C++11 起);
  7. a.m,对象成员表达式,除了 m 是成员枚举项或非静态成员函数,或者 a 是右值而 m 是对象类型的非静态数据成员的情况;
  8. p->m,内建的指针成员表达式,除了 m 是成员枚举项或非静态成员函数的情况;
  9. a.*mp,对象的成员指针表达式,其中 a 是左值且 mp 是数据成员指针;
  10. p->*mp,内建的指针的成员指针表达式,其中 mp 是数据成员指针;
  11. a, b,内建的逗号表达式,其中 b 是左值;
  12. a ? b : c,对某些 b 和 c 的三元条件表达式(例如,当它们都是同类型左值时,但细节见定义);
  13. 字符串字面量,例如 “Hello, world!”;
  14. 转换到左值引用类型的转型表达式,例如 static_cast<int&>(x);
  15. 返回类型是到函数的右值引用的函数调用表达式或重载的运算符表达式;(C++11 起)
  16. 转换到函数的右值引用类型的转型表达式,如 static_cast<void (&&)(int)>(x)。(C++11 起)

性质:

  1. 可以通过内建的取址运算符取左值的地址:&++i[1] 及 &std::endl 是合法表达式。
  2. 可修改的左值可用作内建赋值和内建复合赋值运算符的左操作数。
  3. 左值可以用来初始化左值引用;这会将一个新名字关联给该表达式所标识的对象。

四.纯右值

满足下列条件之一:

1.本身就是纯粹的字面值,如3,false,12.13

2.求值结果相当于字面值或是一个不具名的临时对象

下列表达式是纯右值表达式:

  1. (除了字符串字面量之外的)字面量,例如 42、true 或 nullptr;
  2. 返回类型是非引用的函数调用或重载运算符表达式,例如 str.substr(1, 2)、str1 + str2 或 it++;
  3. a++ 和 a–,内建的后置自增与后置自减表达式;
  4. a + b、a % b、a & b、a << b,以及其他所有内建的算术表达式;
  5. a && b、a || b、!a,内建的逻辑表达式;
  6. a < b、a == b、a >= b 以及其他所有内建的比较表达式;
  7. &a,内建的取地址表达式;
  8. a.m,对象成员表达式,其中 m 是成员枚举项或非静态成员函数[2],或其中 a 是右值且 m 是非- - 引用类型的非静态数据成员 (C++11 前);
  9. p->m,内建的指针成员表达式,其中 m 是成员枚举项或非静态成员函数[2];
  10. a.*mp,对象的成员指针表达式,其中 mp 是成员函数指针[2],或其中 a 是右值且 mp 是数据成员指针 (C++11 前);
  11. p->*mp,内建的指针的成员指针表达式,其中 mp 是成员函数指针[2];
  12. a, b,内建的逗号表达式,其中 b 是右值;
  13. a ? b : c,对某些 b 和 c 的三元条件表达式(细节见定义);
  14. 转换到非引用类型的转型表达式,例如 static_cast(x)、std::string{} 或 (int)42;
  15. this 指针;
  16. 枚举项;
  17. 非类型模板形参,除非它的类型是类或 (C++20 起)左值引用类型;
  18. lambda 表达式,例如 [](int x){ return x * x; };(C++11 起)
  19. requires 表达式,例如 requires (T i) { typename T::type; };(C++20 起)
  20. 概念的特化,例如 std::equality_comparable (C++20 起)

性质:

纯右值不具有多态:它所标识的对象的动态类型始终是该表达式的类型。

非类非数组的纯右值不能有 cv 限定,除非它被实质化以绑定到 cv 限定类型的引用 (C++17 起)。(注意:函数调用或转型表达式可能生成非类的 cv 限定类型的纯右值,但它的 cv 限定符通常被立即剥除。)

纯右值不能具有不完整类型(除了类型 void(见下文),或在 decltype 说明符中使用之外)

纯右值不能具有抽象类类型或它的数组类型。

易混:

++i是左值,i++是右值

前者,对i加1后再赋给i,最终的返回值就是i,所以,++i的结果是具名的,名字就是i;而对于i++而言,是先对i进行一次拷贝,将得到的副本作为返回结果,然后再对i加1,由于i++的结果是对i加1前i的一份拷贝,所以它是不具名的。

假设自增前i的值是6,那么,++i得到的结果是7,这个7有个名字,就是i;而i++得到的结果是6,这个6是i加1前的一个副本,它没有名字,i不是它的名字,i的值此时也是7。可见,++i和i++都达到了使i加1的目的,但两个表达式的结果不同。

解引用表达式 * p是左值,取地址表达式 &a 是纯右值。

&(*p) 一定是正确的,因为 *p得到的是p指向的实体,&( *p)得到的就是这一实体的地址,正是p的值。由于 &(*p)的正确,所以 *p是左值。而对&a而言,得到的是a的地址,相当于unsigned int型的字面值,所以是纯右值。

a+b、a&&b、ab 都是纯右值

a+b得到的是不具名的临时对象,而 a&&b 和 ab 的结果非 true 即 false,相当于字面值。

五.将亡值

在C++11之前的右值和C++11中的纯右值是等价的。C++11中的将亡值是随着右值引用的引入而新引入的。换言之,“将亡值”概念的产生,是由右值引用的产生而引起的,将亡值与右值引用息息相关。所谓的将亡值表达式,就是下列表达式:

  • 返回右值引用的函数的调用表达式
  • 转换为右值引用的转换函数的调用表达式

在C++11中,我们用左值去初始化一个对象或为一个已有对象赋值时,会调用拷贝构造函数或拷贝赋值运算符来拷贝资源(所谓资源,就是指new出来的东西),而当我们用一个右值(包括纯右值和将亡值)来初始化或赋值时,会调用移动构造函数或移动赋值运算符来移动资源,从而避免拷贝,提高效率。当该右值完成初始化或赋值的任务时,它的资源已经移动给了被初始化者或被赋值者,同时该右值也将会马上被销毁(析构)。

也就是说,当一个右值准备完成初始化或赋值任务时,它已经“将亡”了。而上面1)和2)两种表达式的结果都是不具名的右值引用,它们属于右值。

又因为

1)这种右值是与C++11新生事物——“右值引用”相关的“新右值”

2)这种右值常用来完成移动构造或移动赋值的特殊任务,扮演着“将亡”的角色,所以C++11给这类右值起了一个新的名字——将亡值。

下列表达式是将亡值表达式:

  1. 返回类型为对象的右值引用的函数调用或重载运算符表达式,例如 std::move(x);
  2. a[n],内建的下标表达式,它的操作数之一是数组右值;
  3. a.m,对象成员表达式,其中 a 是右值且 m 是非引用类型的非静态数据成员;
  4. a.*mp,对象的成员指针表达式,其中 a 是右值且 mp 是数据成员指针;
  5. a ? b : c,对某些 b 和 c 的三元条件表达式(细节见定义);
  6. 转换到对象的右值引用类型的转型表达式,例如 static_cast<char&&>(x);
  7. 在临时量实质化后,任何指代该临时对象的表达式。(C++17 起)

性质:

1.与右值相同。

2.与泛左值相同。

特别是,与所有的右值类似,亡值可以绑定到右值引用上,而且与所有的泛左值类似,亡值可以是多态的,而且非类的亡值可以有 cv 限定。

六.注意

1)字符串字面值是左值。

不是所有的字面值都是纯右值,字符串字面值是唯一例外。

早期C++将字符串字面值实现为char型数组,实实在在地为每个字符都分配了空间并且允许程序员对其进行操作,

cout<<&("abc")<<endl;
const char *p_char="abc";//注意不是char *p_char=&("abc");

这样的代码都是可以编译通过的。

2)具名的右值引用是左值,不具名的右值引用是右值。

void foo(X&& x) 
{ 
    X anotherX = x; //后面还可以访问x
}

上面X是自设计的类型,并且,其有一个指针成员p指向了在堆中分配的内存;参数x是X的右值引用。如果将x视为右值,那么,X another X = x;一句将调用X类的移动构造函数,而我们知道,这个移动构造函数的主要工作就是将x的p指针的值赋给anotherX的p指针,然后将x的p指针置为nullptr。而在后面,我们还可以访问x,也就是可以访问x.p,而此时x.p已经变成了nullptr,这就可能发生意想不到的错误。

3)注释

①只有当存在两个或两个以上的运算对象时才需要运算符连接,单独的运算对象也可以是表达式,例如上面提到的字面值和变量。

②确切说,是表达式的结果的值类别,但我们一般不刻意区分表达式和表达式的求值结果,所以这里称“表达式的值类别”。

③当我们将函数名作为一个值来使用时,该函数名自动转换为指向对应函数的指针。

④关于右值引用本身,没什么可说的,就是指可以绑定到右值上的引用,用"&&"表示,如int&&rra=6;。相比之下,与右值引用相关的一些主题,如移动语义、引用叠加、完美转发等,更值得我们深入探讨。这些内容,在下在后续文章中都会详细介绍。

⑤前提是该右值(如自定义的类X)有移动构造函数或移动赋值运算符可供调用(有时候是没有的,关于这些知识,后续文章在讲移动构造函数和移动赋值运算符时会详述)。

⑥在本文的例二中,如果将get_a_X()的返回值由X的右值引用改为X对象,则get_a_X()是纯右值表达式(如前所述,返回非引用类型的函数调用是纯右值),此时Foo(get_a_X());一句调用的仍然是类X的移动构造函数,这就是一个纯右值完成移动构造的例子。

到此这篇关于详解C++中的左值,纯右值和将亡值的文章就介绍到这了,更多相关C++值内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 详解C++中的左值,纯右值和将亡值

本文链接: https://www.lsjlt.com/news/168474.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 详解C++中的左值,纯右值和将亡值
    目录引入一.表达式二.值类别三.左值四.纯右值五.将亡值六.注意引入 C++中本身是存在左值,右值的概念,但是在C11中又出现了左值,纯右值,将亡值得概念;这里我们主要介绍这些值的概...
    99+
    2024-04-02
  • C++中左值和右值的区别详解
    目录左值右值定义:特性左值引用, 右值引用总结左值右值定义: 左值指的是既能够出现在等号左边也能出现在等号右边的变量(或表达式),右值指的则是只能出现在等号右边的变量(或表达式). ...
    99+
    2024-04-02
  • C++中左值和右值的区别是什么
    今天给大家介绍一下C++中左值和右值的区别是什么。文章的内容小编觉得不错,现在给大家分享一下,觉得有需要的朋友可以了解一下,希望对大家有所帮助,下面跟着小编的思路一起来阅读吧。左值右值定义:左值指的是既能够出现在等号左边也能出现在等号右边的...
    99+
    2023-06-29
  • C++11怎么用static_cast将左值转换为右值
    这篇文章主要讲解了“C++11怎么用static_cast将左值转换为右值”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C++11怎么用static_cast将左值转换为右值”吧!使用std...
    99+
    2023-06-19
  • C++左值与右值,右值引用,移动语义与完美转发详解
    目录C++——左值与右值、右值引用、移动语义与完美转发一、左值和右值的定义二、如何判断一个表达式是左值还是右值(大多数场景)三、C++右值引用四、std::m...
    99+
    2024-04-02
  • C语言入门篇--变量的左值和右值
    目录1.普通变量2.指针变量2.1初步理解2.2深入理解在看此块内容前可以先看看内存及地址相关内容,更容易理解。 1.普通变量 一个变量是有三个属性: 1.变量的空间。 2.变量的内...
    99+
    2024-04-02
  • C++ 函数左值和右值参数传递的性能比较
    左值和右值参数传递的性能差异左值参数传递存在副本开销,降低性能,尤其是对于大型对象。右值参数传递避免副本开销,提升性能,尤适用于临时对象或字面量。 C++ 函数左值和右值参数传递的性能...
    99+
    2024-04-21
    左值 右值参数 c++
  • 一篇文章弄懂C++左值引用和右值引用
    目录1. 左值和右值 2. 左值引用 3. 右值引用 3.1 出现 3.2 概念 3.3 应用 3.3.1 右值引用绑定到左值上 3.3.2 std::move()本质 3.3.3 ...
    99+
    2024-04-02
  • 详解C++右值引用
    目录概述移动语义(Move Semantics)完美转发(Perfect Forwarding)概述 在C++中,常量、变量或表达式一定是左值(lvalue)或右值(rvalue)。...
    99+
    2024-04-02
  • C语言中什么是左值引用与右值引用
    这篇文章主要介绍“C语言中什么是左值引用与右值引用”,在日常操作中,相信很多人在C语言中什么是左值引用与右值引用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C语言中什么是左值引用与右值引用”的疑惑有所帮助!...
    99+
    2023-06-16
  • C++ 函数左侧值引用和右侧值引用参数的区别
    c++++中左侧和右侧值引用参数的不同之处如下:左侧值引用 (&) 指向已有对象,用于修改其状态。右侧值引用 (&&) 指向临时对象,用于获取或传递其数据。 C...
    99+
    2024-04-19
    参数 函数 引用 c++
  • C++中左值与右值的概念与应用方法是什么
    这篇文章主要讲解了“C++中左值与右值的概念与应用方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C++中左值与右值的概念与应用方法是什么”吧!什么是左值与右值?左值(Lvalue)...
    99+
    2023-07-05
  • 如何解析C++左值与右值之间共同与不同点
    这篇文章将为大家详细讲解有关如何解析C++左值与右值之间共同与不同点,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。C++编程语言与C语言相比有很多不同之处,而且这些不同的地方有都体现着非常重...
    99+
    2023-06-17
  • 详解C++ 左值引用与 const 关键字
    左值引用是已定义的变量的别名,其主要用途是用作函数的形参,将 const 关键字用于左值引用时,其在初始化时可接受的赋值形式变得更加广泛了,这里来总结一下。 左值引用是已定义的变量的...
    99+
    2024-04-02
  • C++ 右值引用与 const 关键字详解
    C++中的const关键字的用法非常灵活,而使用const将大大改善程序的健壮性,const关键字是一种修饰符。修饰符本身,并不产生任何实际代码。就 const 修饰符而言,它用来告...
    99+
    2024-04-02
  • C++11学习之右值引用和移动语义详解
    目录左值引用与右值引用1、左值与右值2、纯右值、将亡值3、左值引用与右值引用4、右值引用和 std::move 使用场景引用限定符const 和引用限定符移动语义—std...
    99+
    2023-02-23
    C++11右值引用 移动语义 C++11右值引用 C++11 移动语义
  • C++ 左值引用与一级指针示例详解
    将左值引用用于一级指针时,有以下几种用法: //方式一:引用一级指针,常规用法 int a = 5; int * pa = &a; int * &rpa = pa; ...
    99+
    2024-04-02
  • 关于ECMAScript中的原始值和引用值详解
    目录前言什么是动态属性 值的复制 判断值类型 总结前言 这应该是很基础的 JavaScript 的知识点,但估计很多小伙伴都只是简单带过,到面试时一问三不知。这里结合我之前的笔记,再...
    99+
    2024-04-02
  • C++中的最小值函数详解
    C++中的最小值函数详解在C++的标准库中,有一个名为“min”的函数,它用于返回两个给定参数中较小的一个。这个函数在C++中很常用,因为在编程时我们经常需要比较两个变量中的最小值。在本文中,我们将详细介绍C++中的最小值函数,包括有关如何...
    99+
    2023-11-18
    C++ 详解 最小值函数
  • 如何进行C++ 11右值引用的理解
    本篇文章给大家分享的是有关如何进行C++ 11右值引用的理解,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。C++ 11中引入的一个非常重要的概念就是右值引用。理解右值引用是学习...
    99+
    2023-06-17
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作