左右值引用和移动语义
文章首发公众号:iDoitnow
1. 左右值和左右值引用
什么是左值、右值呢?一种极不严谨的理解为:在赋值的时候,能够被放到等号左边的值为左值,放在右边的值为右值。例如:
int sum(int x, int y){return x + y;}int a = 1; //a为左值,常数1为右值
int b = a + a; //b为左值,表达式a+a为右值
int c = sum(a, a);//c为左值,但函数sum(a, a)返回值为右值
通过上面的例子,常数a、表达式(a+a)和函数sum(a+a)返回值他们都是临时值,这些值都保存在寄存器中,无法取到他们的地址;而对于a、b和c为具体的变量名,存储在内存中,可以取到其地址。因此一般情况下可以根据能否取到地址,来区分左值和右值。
在了解左值和右值之前,我们首先要知道表达式的概念:由运算符和运算对象构成的计算式(类似数学中的算术表达式)。表达式是可以求值的,因此根据表达式值的类别,可以对其进行分类(准确的来说,是表达式的结果的值类别,但我们一般不刻意区分表达式和表达式的求值结果,所以这里称“表达式的值类别”。),C++11之后将表达式定义了五种类型:
-
lvalue(Left-hand-side value,左值)
-
prvalue(Pure rvalue,纯右值)
-
xvalue(eXpiring value,将亡值)
-
rvalue(Right-hand-side value,右值)
-
glvalue(Generalized lvalue,泛左值)
它们之间的关系如下图所示:
C++11中将表达式按值类别可以分为左值、将亡值和纯右值。其中,左值和将亡值合称为泛左值,纯右值和将亡值合称为右值。
随着移动语义(后面我们会详细介绍)引入到 C++11 之中,值类别被重新进行了定义,C++之父Bjarne Stroustrup在《“New” Value Terminology》中给出以区别表达式的两种独立的性质:
- 拥有身份 (identity):可以确定表达式是否与另一表达式指代同一实体,例如通过比较它们所标识的对象或函数的(直接或间接获得的)地址;
- 可被移动:移动构造函数、移动赋值运算符或实现了移动语义的其他函数重载能够绑定于这个表达式。
C++11 中:
- 拥有身份且不可被移动的表达式被称作左值(lvalue)表达式;
- 拥有身份且可被移动的表达式被称作将亡值 (xvalue)表达式;
- 不拥有身份且可被移动的表达式被称作纯右值 (prvalue)表达式;
1.1 左值
一般情况下,左值我们可以简单地理解理解为:能够使用&取地址的表达式。
常见的左值有:
- 变量名
- 函数名
- 返回左值引用的函数调用
- 前置自增/减的运算符链接的表达式(如
++i/--i) - 内置的赋值表达式(如
a=b,a+=1) - 字符串等。
【注:字符串是可以取地址的,因此字符串常量也属于左值】
1.2 纯右值
纯右值:表达式本身就是纯粹的字面值(如1,ture,1.0);或者,该表达式求值结果相当于一个字面值或一个不具名的临时对象。
常见的纯右值有:
- 除字符串字面值以外的字面值
- 返回非引用类型的函数调用
- 后置自增/减的运算符链接的表达式(如
i++/i--) - 算术/逻辑/比较表达式(如
a+b,a&&b,a==b) - 取地址表达式(如
&a)
1.3 将亡值
将亡值是在C++11中引进来的,顾名思义,就即将销毁的东西。将亡值的产生与右值引用的产生而引起的,对于将亡值我们常用到的有:
- 返回类型是右值引用的函数调用或重载运算符的表达式(如
std::move(x)) - 转换为右值引用的转换函数的调用表达式(如
static<int&&>(a))
1.4 左右值引用
左值引用就是对左值的引用。它的形式如:T&,根据const属性可以分为两种:
- const左值引用
- 非const左值引用
例如:
int a = 1;
int& la = a;//la为a的左值引用(非const左值引用)
la = 2;//la为非const左值引用,可以修改它的值const int& c_la = a;//c_la为a的左值引用(const左值引用)
c_la = 2;//该语法错误,c_la为const左值引用,不可以修改它的值
右值引用就是对右值的引用,通过T&&来表示。右值的引用只能绑定到右值上。
2. 移动语义
在未出现右值引用之前,我们在函数调用传参的时候,在某些时候可以使用按引用传递参数,减少参数多的拷贝对资源的消耗,提高程序的运行效率。当我们在处理包含大量数据的对象时,移动语义显的尤为重要。
2.1 std::move
如何将一个左值转换为一个右值呢?C++11在头文件utility中声明了std::move()函数,该函数的作用就是类型转换,通过它,我们可以 把一个左值,将其标记为右值。move()不做任何资源转移的操作,只是产生一个将亡值表达式来标识参数x,其完全等同于static_cast<T&&>(x)。例如:
int a = 1;
int&& r_a = a; //错误,右值引用只能绑定到右值上,而a是一个左值
int&& r_a = std::move(b); //正确, std::move(a) 是一个右值,可以用右值引用绑定
2.2 移动构造函数
一个类 T 的首个形参是 T&&、const T&&、volatile T&& 或 const volatile T&&,且没有其他形参,或剩余形参都有默认值。
具体的形式如下:
T (T &&) //移动构造函数的典型声明形式
T (T &&) = default; //强制编译器生成移动构造函数。
T (T &&) = delete; //避免隐式生成移动构造函数。
示例:
#include <string>
#include <iostream>
#include <utility>class A
{private:std::string s;public:A(std::string str = "A()") : s(str) {std::cout<<s<<"的构造函数\n";}A(A&& o) : s(std::move(o.s)) {std::cout<<s<<"的移动构造函数\n";}~A(){std::cout<<s<<"的析构函数\n";}
};A f(A a) { return a; }int main()
{A a1(f(A("a")));// 按值返回时,从函数形参移动构造它的目标A a2(std::move(a1));// 从亡值移动构造
}
2.3 移动赋值运算符
一个类 T 的移动赋值运算符是名为 operator=的非模板非静态成员函数,它接受恰好一个 T&&、const T&&、volatile T&& 或 const volatile T&& 类型的形参。
具体的形式如下:
T & T ::operator= (T &&) //移动赋值运算符的典型声明
T & T ::operator= (T &&) = default; //强制编译器生成移动赋值运算符
T & T ::operator= (T &&) = delete; //避免隐式移动赋值
示例:
#include <string>
#include <iostream>
#include <utility>class A
{private:std::string s;public:A(std::string str = "A()") : s(str) {std::cout<<s<<"的构造函数\n";}~A(){std::cout<<s<<"的析构函数\n";}A& operator=(const A& other){s = other.s;std::cout << "复制赋值\n";return *this;}A& operator=(A&& other){s = std::move(other.s);std::cout << "移动赋值\n";return *this;}
};A f(A a) { return a; }int main()
{A a1("a1"), a2("a2");std::cout << "尝试从右值临时量移动赋值 A\n";a1 = f(A("a")); // 从右值临时量移动赋值std::cout << "尝试从亡值移动赋值 A\n";a2 = std::move(a1); // 从将亡值移动赋值
}
参考文献
C++ Primer Plus(第六版) - 第18章 探讨C++新标准
C++ 参考手册
相关文章:
左右值引用和移动语义
文章首发公众号:iDoitnow 1. 左右值和左右值引用 什么是左值、右值呢?一种极不严谨的理解为:在赋值的时候,能够被放到等号左边的值为左值,放在右边的值为右值。例如: int sum(int x, int y){return x y;…...
一起学习用Verilog在FPGA上实现CNN----(七)全连接层设计
1 全连接层设计 1.1 Layer 进行线性计算的单元layer,原理图如图所示: 1.2 processingElement Layer中的线性计算单元processingElement,原理图如图所示: processingElement模块展开原理图,如图所示,包含…...
tomcat打debug断点调试
windows debug调试 jdk版本:1.8.0_181 tomcat版本:apache-tomcat-9.0.68.0 idea版本:2020.1 方法一 修改catalina.bat 在%CATALINA_HOME%\bin\catalina.bat中找到 set “JAVA_OPTS%JAVA_OPTS% -Djava.protocol.handler.pkgsorg.apache…...
如果持有互斥锁的线程没有解锁退出了,该如何处理?
文章目录如果持有互斥锁的线程没有解锁退出了,该如何处理?问题引入PTHREAD_MUTEX_ROBUST 和 pthread_mutex_consistent登场了结论:如果持有互斥锁的线程没有解锁退出了,该如何处理? 问题引入 看下面一段代码…...
信息论绪论
本专栏针包含信息论与编码的核心知识,按知识点组织,可作为教学或学习的参考。markdown版本已归档至【Github仓库:information-theory】,需要的朋友们自取。或者关注公众号【AIShareLab】,回复 信息论 也可获取。 文章目…...
Buffer Status Reporting(BSR)
欢迎关注同名微信公众号“modem协议笔记”。 以一个实网中的异常场景开始,大概流程是有UL data要发送,UE触发BSR->no UL grant->SR->no UL grant->trigger RACH->RACH fail->RLF->RRC reestablishment:简单描述就是UE触…...
代码随想录LeetCode | 单调栈问题
前沿:撰写博客的目的是为了再刷时回顾和进一步完善,其次才是以教为学,所以如果有些博客写的较简陋,是为了保持进度不得已而为之,还请大家多多见谅。 预:看到题目后的思路和实现的代码。 见:参考…...
C++之可调用对象、bind绑定器和function包装器
可调用对象在C中,可以像函数一样调用的有:普通函数、类的静态成员函数、仿函数、lambda函数、类的非静态成员函数、可被转换为函数的类的对象,统称可调用对象或函数对象。可调用对象有类型,可以用指针存储它们的地址,可…...
MongoDB--》文档查询的详细具体操作
目录 统计查询 分页列表查询 排序查询 正则的复杂条件查询 比较查询 包含查询 条件连接查询 统计查询 统计查询使用count()方法,其语法格式如下: db.collection.count(query,options) ParameterTypeDescriptionquerydocument查询选择条件optio…...
网络协议(六):网络层
网络协议系列文章 网络协议(一):基本概念、计算机之间的连接方式 网络协议(二):MAC地址、IP地址、子网掩码、子网和超网 网络协议(三):路由器原理及数据包传输过程 网络协议(四):网络分类、ISP、上网方式、公网私网、NAT 网络…...
热启动预示生态起航的Smart Finance,与深度赋能的SMART通证
2023年初加密市场的回暖,意味着各个赛道都将在新的一年里走向新的叙事。最近,我们看到GameFi赛道也在市场回暖的背景下,逐渐走出阴霾。从融资数据上看,1月获得融资的GameFi项目共12个,融资突破8000万美元,1…...
提分必练,中创教育PMP全真模拟题分享
湖南中创教育每日五题分享来啦,“日日行,不怕千万里;常常做,不怕千万事。”,每日五题我们练起来! 1、在系统测试期间,按已识别原因的类型或类别记录了失败测试的数量。项目经理首先需要从最大故…...
PID控制算法基础介绍
PID控制的概念 生活中的一些小电器,比如恒温热水器、平衡车,无人机的飞行姿态和飞行速度控制,自动驾驶等等,都有应用到 PID——PID 控制在自动控制原理中是一套比较经典的算法。 为什么需要 PID 控制器呢? 你一定用…...
Ajax 学习笔记
一、Ajax1.1 什么是AjaxAJAX Asynchronous JavaScript and XML(异步的JavaScript和XML)。Ajax是一种在无需加载整个网页的情况下,能够更新部分网页的技术,它不是一种新的编程语言,而是一种用于创建更好更快以及交互性更强的Web应用程序的技术…...
力扣解法汇总1234. 替换子串得到平衡字符串
目录链接: 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目: https://github.com/September26/java-algorithms 原题链接:力扣 描述: 有一个只含有 Q, W, E, R 四种字符,且长度为 n 的字符串。 假如在该…...
C++关键字之const、inline、static
C 关键字总结 1.const const是 constant 的缩写,本意是不变的、不易改变的意思。在C中用来修饰内置类型变量,自定义对象,成员函数,返回值,函数参数使用如下: //修饰普通类型变量 const int a 7; int ba;…...
【成为架构师课程系列】怎样进行概念架构(Conceptual Architecture)?
目录 前言 什么是概念架构 概念架构阶段的3个步骤 初步设计 高层分割 分层式概念服务架构 Layer:逻辑层 Tier: 物理层 按通用性分层 技术堆叠 考虑非功能需求 【禅与计算机程序设计艺术:更多阅读】 前言 胜兵先胜而后求战,败兵先站而后求胜。…...
PostgreSQL的下载安装教程(macOS、Windows)
postgresql是GIS服务端几乎不可避免要打交道的数据库。因为mysql的空间扩展真是不尽人意。所以想要学会GIS服务端知识,postgresql(下文简称pg)你是必须要会的。 首先要知道,pg是一个空间数据库,和普通数据库不同的是pg支持空间数据的存储与操作。这里所谓的空间数据一般指…...
98年的确实卷,公司新来的卷王,我们这帮老油条真干不过.....
都说00后躺平了,但是有一说一,该卷的还是卷。这不,前段时间我们公司来了个00后,工作没两年,跳槽到我们公司起薪18K,都快接近我了。后来才知道人家是个卷王,从早干到晚就差搬张床到工位睡觉了。 …...
软件架构知识2-系统复杂度
架构设计的真正目的:是为了解决软件系统复杂度带来的问题,一个解决方案。 系统复杂度,如何入手: 1、通过熟悉和理解需求,识别系统复杂性所在的地方,然后针对这些复杂点进行架构设计。 2、架构设计并不是要…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...
el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...
WebRTC从入门到实践 - 零基础教程
WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC? WebRTC(Web Real-Time Communication)是一个支持网页浏览器进行实时语音…...
django blank 与 null的区别
1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是,要注意以下几点: Django的表单验证与null无关:null参数控制的是数据库层面字段是否可以为NULL,而blank参数控制的是Django表单验证时字…...
