C++中的左值和右值

目录

一. 左值和右值的概念

1. 左值

1.1 可修改的的左值

1.2 不可修改的左值

右值

二. 左值引用和右值引用

1. 左值引用

2. 右值引用

主要用途

          1. 移动语义

          2. 完美转发

             2.1 引用折叠

             2.2 std::forward


一. 左值和右值的概念

什么是左值和右值

1. 左值

        左值是一个表示数据的表达式,它代表一个具名的内存位置,程序可以获取其地址,可以通过地址访问它们,是可被引用的数据对象。左值又可分为可修改的的左值和不可修改的左值。

1.1 可修改的的左值

一开始,左值其实就是指可出现在赋值语句左边的表达式,它们的值是可以被修改的,当然这是最初的概念了。比如常规变量名、解除引用的指针、数组元素、引用、对象成员都是左值。

	int a;a = 10;int* ptr = &a;*ptr = 100;vector<int> vec(5,10);vec[2] = 20;int& c = a;c = 1000;

1.2 不可修改的左值

后面随着关键字const的引入,这个左值的概念发生了变化 。因为虽然不能对const变量赋值,但是可以获取其地址。

	const int a = 10;const int* ptrA = &a; //可以通过地址访问它a = 100;              //错误

  • 右值

       前面说完了左值,那右值是什么呢?通过排他性来定义的话,每个表达式不是左值就是右值。 右值代表一个临时的值,不能被取地址,不能被修改,是不能出现在赋值语句的左边的。

       右值包括字面常量(C-style字符串除外,它表示地址)、临时对象、诸如x+y等表达式、返回值的函数(条件是该函数返回的不是引用)等。

int foo() {return 1;
}void test01() {int a = 1;int b = 2;int c = a + b;int d = foo();
};

       比如上面的a,b,c,d这几个变量都是左值,但是a+b和foo()都是右值。 我们知道是无法写成如下这样的:

    a + b = 3;foo() = 2;

二. 左值引用和右值引用

1. 左值引用

左值引用也就是传统的C++引用,通过&符号来声明,它可以使得标识符关联到左值。引用是已定义变量的别名,例如下面将b作为a变量的别名,b和a指向相同的值和内存单元

	int a = 1;int& b = a;

引用必须在创建时就要进行初始化,一旦与某个变量关联起来,就将一直相关联,不会中途关联到其他变量

	int a = 1;//错误,因为引用必须在创建时就关联变量int& b;b = a; 

引用的主要用途是用来作为函数的形参,通过将引用变量用作参数,函数将使用原始数据,而不是其副本。

2. 右值引用

在C++11引入了右值引用(rvalue reference)的概念,通过&&符号来声明,右值引用可以关联到右值。

	int && a = 1;cout << "a = " << a << ", " << &a<< endl;

  • 主要用途

引入右值引用的目的是什么呢?其主要目的之一是实现移动语义。

          1. 移动语义

移动语义是一种优化技术,它允在对象之间转移资源的所有权,而不是进行资源的复制或者拷贝。移动语义这种技术正是通过使用右值引用来实现的。

先说一下为什么会诞生移动语义这个东西:当调用拷贝构造函数或赋值运算符时会对对象进行拷贝操作,也就是会申请一块新的内存空间,然后把数据复制到新的内存空间当中,但是这对于大型对象来说工作量是很大的,当然在一些情况下这是必要的操作,但是在某些场景下,可能其实不用额外拷贝一份,只需要将对象的资源所有权从一个对象转移到另一个对象就可以满足要求了。举一个例子(取自于C++ Primer Plus):

vector<string> allcaps(const vector<string>& vs) {vector<string> temp;//code that store an all-uppercase version of vs in temp(将vs转换成全大写版本存储在temp中)return temp;
}vector<string> vstr;
//build up a vecor of 20000 strings,each of 1000 characters(构建存储有20000个字符串的vector,每个字符串包含1000个字符)vector<string> vstr_copy(allcaps<vstr>);

可以发现,allocaps()创建了对象temp,这个temp对象管理着20000000个字符;vector和string的拷贝构造函数创建这20000000字符的副本,然后程序再删除allocaps()返回的临时对象temp。这里面做了大量的无用功,这里先是将temp对象的值拷贝给vstr_copy,然后再删除temp对象;如果能够直接将temp对数据的所有权转让给vstr_copy也就是不将temp所管理的20000000个字符复制到新地方,而是保留在原来的地方,并将vstr_copy与之相关联,这样的话,工作量就省掉了很多,效率也就上来了。于是移动语义这个技术就出现了,它便是可以将对象的资源所有权从一个对象转移到另一个对象,而不进行资源的复制的技术。

移动语义得以实现,这其中便离不开右值引用的支持。那右值引用对移动语义的支持体现在什么地方呢:因为需要让编译器知道什么时候需要的是拷贝,什么时候不需要,这就是右值引用发挥作用的地方了。简单来说,就是编译器会根据会根据传进来的参数是左值引用还是右值引用来决定调用拷贝构造函数还是移动构造函数(移动构造函数里将资源从一个对象转移到另一个对象,而不用复制)。

class test {
public:
test(const test& t); //拷贝构造函数
test(test&& t);      //移动构造函数private:int n;
};

总结,移动语义发生,需要两个步骤:

1. 右值引用让编译器知道何时可使用移动语义

2. 编译移动构造函数,使其提供所需的方法。也就是怎么让资源从一个对象转移到另一个对象,而不用复制

          2. 完美转发

完美转发是C++11引入的一种特性,它是指泛型编程(模板编程)中,函数模板能够完全自己模板参数的类型传递给内部调用的其他函数,即参数左右值的属性不会发生变化。

 完美转发能够保留参数的类型和值类别(左值或右值),从而实现更为高效且准确地传递参数,用于解决在函数模板中传递参数时的类型推导问题。它允许将参数以原始的形式传递给另一个函数,而不会引入额外的拷贝或移动操作。

完美转发的实现过程中有借助右值引用和std::forward。

             2.1 引用折叠

C++中,一般右值引用的参数只能接收右值,不能接收左值;但是在函数模板中使用右值引用的参数又不太一样,它既可以接收右值引用,也可以接收左值引用。借助C++的引用折叠规则,只要函数模板的参数类型为 T&&,则 C++ 可以自行准确地判定出实际传入的实参是左值还是右值。

下面这个例子里,虽然模板函数f的参数t声明为了右值引用,但是实际传参是时可以是左值引用。这里“&&”又可成为通用引用

template<class T>
void f(T&& t) {g(t);
};

假设用 A 表示实际传递参数的类型:

  • 当实参为左值或者左值引用(A&)时,函数模板中 T&& 将转变为 A&(A& && = A&)
  • 当实参为右值或者右值引用(A&&)时,函数模板中 T&& 将转变为 A&&(A&& && = A&&)

引用折叠规则:

& + & -> &
& + && -> &
&& + & -> &
&& + && -> &&

可以发现,一旦定义中出现了左值引用,引用折叠规则总是优先将其折叠为左值引用。

             2.2 std::forward

std::forward是一个模板函数,用于在函数模板中将参数原封不动地转发给其他函数,保持参数的值类别(左值引用或右值引用的属性)和const/volatile限定符。

以前面那个例子,在函数模板f里调用函数g时,没有加forward,可以发现调用f时,即使传进去的参数是右值引用,但是从运行结果来看,f里调用函数g()时,传给函数g()的却变成了左值引用,这是因为传入给g()的是一个具名变量参数,具名变量即使被声明为右值类型也不会被当作右值,g()会认为这个值就是是一个左值;参数是左值,使用这个参数时还是会调用拷贝构造函数(如果是class的话)。

void g(int& a) {cout << "左值引用" << endl;
}void g(int&& a) {cout << "右值引用" << endl;
}template<typename T>
void f(T&& t) {g(t);
};void test01() {f(2);    //传入右值引用return;
};int main() {test01();return 0;
}

如果在调用函数g()时,改写成g(forward<T>(t))后,可以看到运行结果变成了预期的“右值引用”,这便是forward提供的完美转发的功劳。

void g(int& a) {cout << "左值引用" << endl;
}void g(int&& a) {cout << "右值引用" << endl;
}template<typename T>
void f(T&& t) {g(forward<T>(t));
};void test01() {f(2);    //传入右值引用return;
};

参数t是一个通用引用,它可以接受左值引用或者左值引用的参数。通过使用std::forward,可以将原参数t封不动地转发给函数g,并保持参数t的左值引用或者左值引用属性不会发生变化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/701647.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux内核源码安装

文章目录 前言查看内核源码包安装内核源码编译内核源码最后 前言 我是醉墨居士&#xff0c;我们安装一下Linux内核源码&#xff0c;方便我们学习Linux内核 也方便我们进行eBPF开发时查看Linux内核的一些信息 查看内核源码包 apt-cache search linux-source安装内核源码 因为…

【vue3语法】开发使用创建项目等

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、vue3创建vue3v2函数式、v3组合式api响应式方法ref、reactive计算属性conputed监听属性wacthvue3 选项式生命周期父子通信父传子defineProps编译宏 子传父de…

互联网加竞赛 机器视觉opencv答题卡识别系统

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 答题卡识别系统 - opencv python 图像识别 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f947;学长这里给一个题目综合评分(每项满分5分…

并查集例题(食物链)C++(Acwing)

代码&#xff1a; #include <iostream>using namespace std;const int N 50010;int n, m; int p[N], d[N];int find(int x) {if(p[x] ! x){int t find(p[x]);d[x] d[p[x]];p[x] t;}return p[x]; }int main() {scanf("%d%d", &n, &m);for(int i 1…

linux前端部署

安装jdk 配置环境变量 刷新配置文件 source profile source /etc/profile tomcat 解压文件 进去文件启动tomcat 开放tomcat的端口号 访问 curl localhsot:8080 改配置文件 改IP,改数据库名字&#xff0c;密码&#xff0c; 安装数据库 将war包拖进去 访问http:…

【Python笔记-设计模式】代理模式

一、说明 代理模式是一种结构型设计模式&#xff0c;提供对象的替代品或其占位符。代理控制着对于原对象的访问&#xff0c;并允许在将请求提交给对象前后进行一些处理。 (一) 解决问题 控制对对象的访问&#xff0c;或在访问对象前增加额外的功能或控制访问 (二) 使用场景…

apidoc接口文档的自动更新与发布

文章目录 一、概述二、环境准备三、接口文档生成1. 下载源码2. 初始化3.执行 四、文档发布五&#xff0c;配置定时运行六&#xff0c;docker运行七&#xff0c;不足与优化分析 一、概述 最近忙于某开源项目的接口文档整理&#xff0c;采用了apidoc来整理生成接口文档。 apidoc…

深度学习系列59:文字识别

1. 简单文本&#xff1a; 使用google加的tesseract&#xff0c;效果不错。 首先安装tesseract&#xff0c;在mac直接brew install即可。 python调用代码&#xff1a; import pytesseract from PIL import Image img Image.open(1.png) pytesseract.image_to_string(img, lan…

MES管理系统生产过程控制的核心要素

MES&#xff08;制造执行系统&#xff09;是为优化制造业生产过程和管理而设计的软件系统&#xff0c;其核心要素包括&#xff1a; 工单管理&#xff1a;工单管理是MES系统最基本的功能之一&#xff0c;它可以跟踪和管理各种类型的工单&#xff0c;如生产工单、维修工单和质量…

Spring篇----第六篇

系列文章目录 文章目录 系列文章目录前言一、spring 支持集中 bean scope?二、spring bean 容器的生命周期是什么样的?三、什么是 spring 的内部 bean?前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男…

Java EE改名Jakarta EE,jakarta对程序开发的影响

一、前言 很多Java程序员在使用新版本的Spring6或者springboot3版本的时候&#xff0c;发现了一些叫jakarta的包。我在阅读开源工作流引擎camunda源代码的时候&#xff0c;也发展了大量jakarta的工程包。 比如&#xff1a;camunda的webapps编译工程就提供了2种方式javax和jaka…

SCI一区 | Matlab实现ST-CNN-MATT基于S变换时频图和卷积网络融合多头自注意力机制的多特征分类预测

SCI一区 | Matlab实现ST-CNN-MATT基于S变换时频图和卷积网络融合多头自注意力机制的故障多特征分类预测 目录 SCI一区 | Matlab实现ST-CNN-MATT基于S变换时频图和卷积网络融合多头自注意力机制的故障多特征分类预测效果一览基本介绍模型描述程序设计参考资料 效果一览 基本介绍…

论文阅读:Ground-Fusion: A Low-cost Ground SLAM System Robust to Corner Cases

前言 最近看到一篇ICRA2024上的新文章&#xff0c;是关于多传感器融合SLAM的&#xff0c;好像使用了最近几年文章中较火的轮式里程计。感觉这篇文章成果不错&#xff0c;代码和数据集都是开源的&#xff0c;今天仔细读并且翻译一下&#xff0c;理解创新点、感悟研究方向、指导…

【杂谈】还能这么骗Github开源者?

起因 StarkNet给Github前5000的账户空投了一波STRK代币,一般有资格获得空投的开发者&#xff0c;大概能获得 110个 STRK 代币&#xff0c;按目前价格计算大概 1500人民币左右。 什么是有资格的开发者呢&#xff1f;按 Starknet要求&#xff0c;如果你给在 GitHub上排名前 5000…

基于SSM的废品买卖回收管理系统(有报告)。Javaee项目。ssm项目。

演示视频&#xff1a; 基于SSM的废品买卖回收管理系统&#xff08;有报告&#xff09;。Javaee项目。ssm项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通过Spri…

Vue+SpringBoot打造开放实验室管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 实验室类型模块2.2 实验室模块2.3 实验管理模块2.4 实验设备模块2.5 实验订单模块 三、系统设计3.1 用例设计3.2 数据库设计 四、系统展示五、样例代码5.1 查询实验室设备5.2 实验放号5.3 实验预定 六、免责说明 一、摘…

设计模式-创建型模式-建造者模式

建造者模式&#xff08;Builder Pattern&#xff09;&#xff1a;将一个复杂对象的构建与它的表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。建造者模式是一种对象创建型模式。 建造者模式一步一步地创建一个复杂的对象&#xff0c;它允许用户只通过指定复杂对象…

windows 中, bash: conda: command not found(已解决)

git bash 中运行conda命令&#xff0c;出现这种错误&#xff0c;原因是你没有在git bash中 配置conda&#xff0c;导致git bash无法找到conda 那就配置一下&#xff0c;找到你的conda的安装位置下的bash.sh文件&#xff0c;一般在安装位置&#xff08;我的安装在C盘的自定义路径…

RocketMQ生产环境常见问题分析与总结

RocketMQ生产环境常见问题分析与总结 如何保证消息不丢失 消息丢失场景 对于跨网络的节点可能会丢消息&#xff0c;因为MQ存盘都会先写入OS的PageCache中&#xff0c;然后再让OS进行异步刷盘&#xff0c;如果缓存中的数据未及时写入硬盘就会导致消息丢失 生产端到Broker端Brok…

nc开发刚导入项目eclipse出现莫名其妙的错误,红叉,感叹号,文件missing

解决类出现红叉 解决感叹号&#xff0c;文件missing 其他问题 右上角的视图&#xff0c;要选择java&#xff0c;如果是javaEE也会有一些文件没有展示出来。