【C++11】左值引用 与 右值引用

定义

左值 / 左值引用

左值(Lvalue): 左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以 对它取地址 + 可以对它赋值左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左值,不能给他赋值,但是可以取它的地址。

左值引用:就是给左值的引用,给左值取别名。

例子:

// 变量 a, b、指针变量 p 和解引用表达式 *p 都是左值int* p = new int(0);int a = 1;const int b = 2;// 下面是对上述左值的引用int*& rp = p;int& ra = a;const int& rb = b;int& pValue = *p;

右值 / 右值引用

右值(Lvalue):右值也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引用返回)等等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能取地址

右值引用:就是对右值的引用,给右值取别名

double x = 1.0, y = 2.5;
// 几种常见的右值
10;
x + y;
fmax(x, y);
// cout << &(x + y) << endl; //对右值不能进行取地址//以下是对上述右值的右值引用
int&& rr1 = 10;
double&& rr2 = x + y;
double&& rr3 = fmax(x, y);

左值引用 / 右值引用 特点比较

左值引用:

  1. 左值引用只能引用左值,不能引用右值。
  2. 但是const左值引用既可引用左值,也可引用右值
// 左值引用只能引用左值,不能引用右值int a = 10;int& ra1 = a;int& ra2 = 10; //编译失败,10是右值// const左值引用既可以引用左值,也可以引用右值const int& ra3 = a;const int& ra4 = 10; // 不报错

右值引用:

  1. 右值引用只能右值,不能引用左值。
  2. 但是右值引用可以move以后的左值。
// 右值引用只能引用右值,不能引用左值int&& r1 = 10;int a = 10;int&& r2 = a; //编译失败: 无法将右值引用绑定到左值// 右值引用可以引用 move 后的左值int&& r3 = std::move(a);

右值引用的应用场景 和 意义

上文了解到 const左值引用 可以引用右值,既然如此,右值引用的意义是什么?
实际上,左值引用有一定的短板,而右值引用可以弥补该缺陷:

首先看下面的代码:

namespace aiyimu
{class string{public:// 构造函数string(const char* str = ""):_size(strlen(str)), _capacity(_size){_str = new char[_capacity + 1];strcpy(_str, str);}void swap(string& s){::swap(_str, s._str); //std::swap -> ::swap::swap(_size, s._size);::swap(_capacity, s._capacity);}// 拷贝构造string(const string& s):_str(nullptr){cout << "string(const string& s) <---> 拷贝构造(深拷贝)" << endl;// 拷贝操作_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}// 移动构造// 以右值引用作参数string(string&& s):_str(nullptr), _size(0), _capacity(0){cout << "string(string&& s) <---> 资源转移" << endl;swap(s);}// 拷贝赋值运算符string& operator=(const string& s){cout << "string& operator=(string s) <---> 拷贝赋值(深拷贝)" << endl;string tmp(s);swap(tmp);return *this;}// 移动赋值运算符string& operator=(string&& s){cout << "string& operator=(string&& s) <---> 移动赋值(资源移动)" << endl;swap(s);return *this;}		// 析构~string(){delete[] _str;_str = nullptr;}private:char* _str;size_t _size;size_t _capacity;};
}

移动构造函数

定义:

移动构造(Move Constructor)是一种特殊的构造函数,它通过接收一个右值引用参数来创建新对象,并从传入的对象中“移动”资源而不是执行深拷贝。

详解:

我们在代码实现中 写了移动构造函数,与右值引用有一定的联系,详解在这里:

移动构造函数


引用构造 的博客中有具体写到:

  • 由于我们实现的 拷贝构造 的参数是const 类型,所以既可以进行左值引用也可以进行右值引用
  • 当存在移动构造时,传入右值优先调用移动构造,否则构造此时的拷贝构造。
  • 对上面的代码,to_string 的 参数是 右值,调用移动构造,但我们讲两种构造都作讨论。

认识了移动构造函数后,对下面的代码:

aiyimu::string to_string(int value)
{aiyimu::string str;// to_string的内容并不重要 // ... ... 这里省略return str;
}int main()
{aiyimu::string ret("114");ret = to_string(514);return 0;
}

当执行的是拷贝构造+拷贝赋值时:

在这里插入图片描述

可以看出拷贝构造时 进行了两次深拷贝

而当执行的是移动构造+移动赋值时:

在这里插入图片描述

此时的右值引用 避免了两次深拷贝,避免了空间浪费。

可以看出 右值引用 配合 移动构造和移动赋值 的作用。


万能引用(引用折叠)

万能引用(Universal Reference)是由C++中的std::forward和模板推导机制引入的概念。它是一种特殊类型的引用,可以接受各种类型的参数(包括左值、右值)。

定义

首先,在C++中,几乎所有的标准 容器类都支持万能引用 的用法。

  1. 如果传递给万能引用的是一个左值,那么万能引用将被推导为左值引用。这意味着它将保留传递给它的参数的左值特性,并且不能绑定到临时对象或右值。

例如:

int x = 42;
int& lvalueRef = x;       // 左值引用
foo(lvalueRef);           // T 被推导为 int&
  1. 如果传递给万能引用的是一个右值,那么万能引用将被推导为普通的右值引用。这意味着它可以绑定到临时对象、右值或将对象视为右值的情况下。

例如:

int&& rvalueRef = 100;    // 普通的右值引用
foo(std::move(rvalueRef)); // T 被推导为 int&&

请注意,万能引用只能 通过模板来声明 。在函数模板中使用万能引用的时候,根据传递的参数类型和值类别,编译器会进行相应的类型推导。


在这里插入图片描述
在这里插入图片描述

查阅发现,C++11 在list类中添加了万能引用的实现。

声明

万能引用的格式是使用&&语法来声明,它的一般形式为:

template<typename T>
void foo(T&& arg) {// 根据 arg 的值类别进行不同的处理if constexpr (std::is_lvalue_reference_v<T>) {// 处理左值std::cout << "左值引用" << std::endl;} else {// 处理右值std::cout << "右值引用" << std::endl;}
}

用法

下面的代码展示万能引用的用法:

// 万能引用
void Func(int& x) { cout << "左值引用" << endl; }
void Func(const int& x) { cout << "const 左值引用" << endl; }void Func(int&& x) { cout << "右值引用" << endl; }
void Func(const int&& x) { cout << "const 右值引用" << endl; }template<typename T>
void PerfectForward(T&& t)
{// 完美转发: 保持t引用对象属性Func(std::forward<T>(t));
}int main()
{PerfectForward(10);           // 右值int a;PerfectForward(a);            // 左值PerfectForward(std::move(a)); // 右值const int b = 8;PerfectForward(b);		      // const 左值PerfectForward(std::move(b)); // const 右值aiyimu::list<aiyimu::string> lt;aiyimu::string s1("hello");lt.push_back(s1);cout << endl;//lt.push_back(aiyimu::string("world"));lt.push_back("world");return 0;
}

在这里插入图片描述
根据执行结果,可以验证万能引用的作用。

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

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

相关文章

网络系统集成实验(五)| 系统集成路由器OSPF动态、综合路由配置

一、前言 该系列文章将会对网络系统集成课程中相关实验进行更新&#xff0c;本篇为第五篇&#xff0c;有关路由器的OSPF、综合路由配置&#xff0c;包括了OSPF的配置实验、单臂路由实验、RIP配置实验、综合实验等。 注意&#xff1a;该实验的后半部分综合实验基于前面的实验&am…

在idea中高并发下的分布式锁以及解决方法

案例:1.互联网秒杀 2.抢优惠卷 3.接口幂 引入pom文件 <packaging>war</packaging><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.2.8.RELEA…

Python 基本数据类型(三)

文章目录 每日一句正能量数值运算数值类型实例String&#xff08;字符串&#xff09; 每日一句正能量 人的相处&#xff0c;靠的是真心&#xff0c;不是套路。合得来的人&#xff0c;坦诚相待&#xff0c;合不来的人&#xff0c;客气寒暄&#xff1b;谁也别给谁冷脸看&#xff…

自然语言处理从入门到应用——预训练模型总览:迁移学习与微调

分类目录&#xff1a;《自然语言处理从入门到应用》总目录 相关文章&#xff1a; 预训练模型总览&#xff1a;从宏观视角了解预训练模型 预训练模型总览&#xff1a;词嵌入的两大范式 预训练模型总览&#xff1a;两大任务类型 预训练模型总览&#xff1a;预训练模型的拓展 …

【夜深人静学数据结构与算法】回溯算法

目录 前言&#xff1a; 回溯算法&#xff1a; 回溯法的常见应用: 回溯法的模板: 回溯法的图解&#xff1a;​ 案例&#xff1a; 77. 组合 - 力扣&#xff08;LeetCode&#xff09; 总结&#xff1a; 前言&#xff1a; 回溯算法是一个比较抽象的算法&#xff0c;因此我们…

基于Springboot+vue的垃圾分类网站设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

Transformer(四)--实现验证:transformer 机器翻译实践

转载请注明出处&#xff1a;https://blog.csdn.net/nocml/article/details/125711025 本系列传送门&#xff1a; Transformer(一)–论文翻译&#xff1a;Attention Is All You Need 中文版 Transformer(二)–论文理解&#xff1a;transformer 结构详解 Transformer(三)–论文实…

【Spring】Bean的作用域与生命周期详情:请简述Spring的执行流程并分析Bean的生命周期?

前言 我们都知道&#xff0c;Spring框架为开发人员提供了很多便捷&#xff0c;这使得开发人员能够更加专注于应用程序的核心业务逻辑&#xff0c;而不需要花费大量时间和精力在技术细节上。作为一个包含众多工具方法的IoC容器&#xff0c;存取JavaBean是其极为重要的一个环节。…

linux运维常用命令(持续更新)

目录 一&#xff1a; 查看指定端口是否被监听 二&#xff1a;查看某个端口/服务相关进程 三&#xff1a;在B机器查看是否可以访问A机器某个端口,查看端口是否开放 四&#xff1a;查看端口占用列表 五&#xff1a;查看端口占用情况 六&#xff1a;查看哪些进程监听了2181端…

OpenCV(加载、修改、保存图像)

目录 1、图像加载 2、显示图像 3、修改图像 4、图像保存 OpenCV官方文档查询地址&#xff1a;OpenCV: OpenCV modules 1、图像加载 加载图像&#xff08;用cv::imread )imread功能是加载图像文件成为一个Mat对象&#xff0c;其中第一个参数表示图像文件名称 第二个参数&…

Debian 环境使用 docker compose 部署 sentry

Debian 环境使用 docker compose 部署 sentry Sentry 简介什么是 Sentry &#xff1f;Sentry 开发语言及支持的 SDKSentry 功能架构 前置准备条件规格配置说明Docker Desktop 安装WSL2/Debian11 环境准备 Sentry 安装步骤docker 部署 sentry 步骤演示过程说明 卸载关闭 Sentry总…

数据库系统概论(三)数据库设计、数据库恢复技术、并发控制

作者的话 前言&#xff1a;总结下知识点&#xff0c;自己偶尔看一看。 一、数据库设计 数据库设计是指对于一个给定的应用环境&#xff0c;构造&#xff08;设计&#xff09;优化的数据库逻辑模式和物理结构&#xff0c;并据此建立数据库及其应用系统 1.1概述 1.1.1数据库设计…