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.hqwc.cn/news/492025.html

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

相关文章

ROS 2的前世今生 | ROS 2学习笔记

自2015年底首次踏入ROS&#xff08;Robot Operating System&#xff09;的世界以来&#xff0c;我在机器人领域的旅程已近九年。这段历程始于团队几位志同道合的朋友在业余时间的自发学习&#xff0c;逐渐演变成成立了一个致力于英特尔硬件平台与ROS框架集成优化的专业团队&…

国漫年番成趋势?但只有这5部最值得看

自从《斗罗大陆》动画爆火之后&#xff0c;越来越多国漫都开始以年番形式播出&#xff0c;每周都能追自己喜欢的动画也是观众们所期待的。但其实年番对制作公司的要求很高&#xff0c;如果技术跟不上难免出现质量下滑的问题。今天就带大家盘点一下目前在播的最值得看的5部国漫年…

冒充医疗科技公司邮件,传播间谍窃密木马

朋友微信联系我&#xff0c;说遇到一个钓鱼邮件&#xff0c;问我有没有兴趣看看&#xff0c;邮件转发给我&#xff0c;如下所示&#xff1a; 邮件伪装成绍兴安迪医疗科技有限公司关于COVID-19疫情订单票据信息&#xff0c;附件是一个恶意程序&#xff0c;通过分析发现邮件附件携…

kitti数据可视化

数据下载 The KITTI Vision Benchmark Suite 这里以 2011_09_26_drive_0005 (0.6 GB)数据为参考&#xff0c;下载[syncedrectified data] [calibration] 数据。 下载完毕之后解压&#xff0c;然后将calibration文件解压后的结果放在如下目录下&#xff0c; 下载kitti2bag包 …

Spring Boot 项目集成camunda流程引擎

使用camunda开源工作流引擎有&#xff1a;通过docker运行、使用springboot集成、部署camunda发行包、基于源代码编译运行等多种方式。 其中&#xff0c;通过源代码编译运行的方式最为复杂&#xff0c;具体参考&#xff1a;https://lowcode.blog.csdn.net/article/details/1362…

好书推荐丨细说Python编程:从入门到科学计算

文章目录 写在前面Python简介推荐图书内容简介编辑推荐作者简介 推荐理由粉丝福利写在最后 写在前面 本期博主给大家推荐一本Python基础入门的全新正版书籍&#xff0c;对Python、机器学习、人工智能感兴趣的小伙伴们快来看看吧~ Python简介 Python 是一种广泛使用的高级、解…

【JavaEE】 spring boot的配置文件详解

spring boot的配置文件详解 文章目录 spring boot的配置文件详解常用配置spring boot的配置文件1. properties 文件2. YAML 文件3. 多环境配置4. 配置文件优先级5. 配置属性注入特殊说明 properties配置文件基本语法 例子peoperties文件的缺点 YML配置文件YML使用yml 配置不同数…

【管理咨询宝藏资料26】某城投集团对标案例分析报告

本报告首发于公号“管理咨询宝藏”&#xff0c;如需阅读完整版报告内容&#xff0c;请查阅公号“管理咨询宝藏”。 【管理咨询宝藏资料26】某城投集团对标案例分析报告 【关键词】战略规划、对标研究、管理咨询 【文件核心观点】 - 市场化城开企业&#xff0c;打造城市综合运…

CAS5.3使用JPA实现动态注册服务

cas同时支持cas协议和OAuth2协议,官方默认是通过扫描json文件的形式注册客户端服务,但是此种方式需要重启服务才能生效,此次我们将使用JPA来完美实现动态注册服务,如果不知道cas如何部署,可以擦看之前的文章 cas-client基于CAS协议客户端搭建-CSDN博客 cas-server5.3自定义密…

SpringBoot 学习笔记

文章目录 一、IoC二、AOP三、bean3.1 bean 生命周期3.2 三种依赖注入方式3.3 bean 线程安全 四、SpringMVC五、常用注解5.1 Scope5.2 PostConstruct 和 PreDestroy5.3 Component 和 Bean5.4 Autowired 和 Resource 六、基于 ApplicationContextAware 实现工厂模式七、事务失效八…

接口自动化测试用例如何设计

说到自动化测试&#xff0c;或者说接口自动化测试&#xff0c;多数人的第一反应是该用什么工具&#xff0c;比如&#xff1a;Python Requests、Java HttpClient、Apifox、MeterSphere、自研的自动化平台等。大家似乎更关注的是哪个工具更优秀&#xff0c;甚至出现“ 做平台的 &…

盛元广通兽医实验室信息管理系统

兽医实验室信息管理系统在区域发展中扮演着关键角色&#xff0c;为动物卫生、兽医科研、医学教育和疾病防控等方面提供了有力的支持。实验室信息管理系统有助于建立全面的卫生防疫监测网络&#xff0c;从实验室用户、市级、省级和部级用户按级别分级上报&#xff0c;能够及时监…