C++右值引用(左值表达式、右值表达式)(移动语义、完美转发(右值引用+std::forward))(有问题悬而未决)

news2025/1/17 23:17:59

文章目录

    • 什么是右值?
    • 是什么是右值引用?
    • 什么是移动语义?
    • 什么是完美转发?(右值引用+std::forward)

什么是右值?

在 C++ 中,表达式可以分为左值表达式和右值表达式。左值表达式指的是可以出现在赋值语句左边的表达式,例如变量、数组元素、结构体成员等;右值表达式指的是不能出现在赋值语句左边的表达式,例如常量、临时对象、函数返回值等。

右值是指将要被销毁的临时对象或者没有名字的临时对象。例如,一个返回临时对象的函数调用表达式、一个匿名对象、一个类型转换表达式等都是右值表达式,它们都是将要被销毁的临时对象或者没有名字的临时对象。

右值的特点是它们没有持久的身份,不能被取地址,不能被修改,只能被使用一次。因此,右值引用的主要作用是支持移动语义和完美转发,从而提高程序的效率。

在 C++11 中,引入了右值引用和移动语义的概念,使得程序可以更好地利用右值,提高程序的效率。

下面是一些右值的例子:

int a = 1;  // a 是左值,1 是右值
int b = a + 2;  // a + 2 是右值,b 是左值
int* p = &a;  // &a 是右值,p 是左值
int c = func();  // func() 是右值,c 是左值
int d = std::move(a);  // std::move(a) 是右值,d 是左值

在上面的例子中,1、2、&a、func()、std::move(a) 都是右值,它们都是将要被销毁的临时对象或者没有名字的临时对象。左值 a、b、p、c、d 都是可以被取地址、可以被修改、有持久的身份的对象,它们都是左值。

需要注意的是,一个对象既可以是左值,也可以是右值,这取决于它在表达式中的位置。例如,在赋值语句左边的对象是左值,在赋值语句右边的对象是右值。

是什么是右值引用?

右值引用是 C++11 引入的新特性,用于支持移动语义和完美转发。右值引用的语法是在类型名后面加上两个引用符号 &&,例如 int&& 表示对一个右值 int 对象的引用。

右值引用的主要作用是支持移动语义,即将一个对象的资源(内存、指针等)移动到另一个对象中,从而避免了不必要的内存拷贝和资源分配。移动语义可以提高程序的效率,特别是当对象较大时,避免了不必要的内存拷贝和资源分配,从而提高了程序的性能。

右值引用还可以用于完美转发,即将一个函数的参数以原样转发给另一个函数,从而避免了不必要的拷贝和转换。完美转发可以提高程序的效率,特别是当函数参数较大或者类型较复杂时,避免了不必要的拷贝和转换,从而提高了程序的性能。

需要注意的是,右值引用只能绑定到一个右值对象,不能绑定到一个左值对象。如果尝试将一个左值对象绑定到一个右值引用上,编译器会报错。

下面是一个右值引用的例子:

#include <iostream>
#include <string>

void print(std::string&& str) {
    std::cout << str << std::endl;
}

int main() {
    std::string s = "Hello, world!";
    print(std::move(s));  // 将左值 s 转换为右值引用
    return 0;
}

在上面的例子中,print 函数的参数是一个右值引用 std::string&&,表示对一个右值 std::string 对象的引用。在 main 函数中,我们定义了一个左值 std::string 对象 s,然后将它转换为右值引用 std::move(s),并将它作为参数传递给 print 函数。

由于 std::move(s) 返回的是一个右值引用,因此可以绑定到 print 函数的参数上。在 print 函数中,我们可以使用 str 来访问传递进来的字符串,而不需要进行不必要的拷贝和转换。

需要注意的是,由于 std::move 只是将一个左值对象转换为右值引用,它并不会移动对象的资源。如果需要移动对象的资源,需要在移动构造函数或移动赋值运算符中使用右值引用。

注意:上面这个例子只是展示右值引用,用普通引用也能达到同样效果,具体右值引用的特殊效用,我们需要看下面的移动语义和完美转发。

什么是移动语义?

移动语义是 C++11 引入的一个新特性,它可以将对象的资源(比如内存、文件句柄等)从一个对象转移到另一个对象,避免了不必要的复制和销毁操作,提高了程序的性能。

在移动语义中,右值引用扮演了重要的角色。只有右值引用才能绑定到临时对象或将要销毁的对象,从而实现资源的转移。如果使用左值引用代替右值引用,就无法实现移动语义的效果。

下面是一个使用右值引用实现移动语义的例子:

#include <iostream>
#include <string>
#include <cstring>
using namespace std;

class MyString
{
public:
    MyString() : m_data(nullptr), m_size(0) {}
    MyString(const char *str) : m_data(new char[strlen(str) + 1]), m_size(strlen(str))
    {
        strcpy(m_data, str);
    }
    MyString(MyString &&other) : m_data(other.m_data), m_size(other.m_size)
    {
        other.m_data = nullptr;
        other.m_size = 0;
    }
    ~MyString()
    {
        delete[] m_data;
    }
    void print() const
    {
        std::cout << m_data << std::endl;
    }

private:
    char *m_data;
    size_t m_size;
};

int main()
{
    MyString s1("Hello, world!");
    s1.print(); // 输出 "Hello, world!"

    cout << "------------------" << endl;

    MyString s2(std::move(s1)); // 将 s1 转移为右值引用
    s1.print();                 // 输出空字符串

    cout << "------------------" << endl;

    s2.print(); // 输出 "Hello, world!"

    cout << "------------------" << endl;

    return 0;
}

在上面的代码中,我们定义了一个 MyString 类,它包含一个字符数组和一个大小成员变量。在类的构造函数中,我们使用 new 运算符为字符数组分配内存,并将字符串复制到数组中。在移动构造函数中,我们将其他对象的指针和大小成员变量移动到当前对象中,并将其他对象的指针和大小成员变量设置为 null 和 0。这样,我们就实现了将一个 MyString 对象的资源转移到另一个对象的功能。在 main 函数中,我们创建了两个 MyString 对象 s1 和 s2,然后将 s1 转移为右值引用,从而实现了移动语义的效果。

上面代码还有点bug,我已经上知乎问了。。。

在这里插入图片描述

什么是完美转发?(右值引用+std::forward)

C++右值引用完美转发是一种技术,用于在函数调用中将参数以原样传递给另一个函数,同时保持参数的值类别(左值或右值)。这种技术可以提高代码的效率和可读性。

在C++11中,引入了右值引用和std::forward函数,使得完美转发成为可能。右值引用是一种新的引用类型,可以绑定到右值(临时对象或表达式的结果),而左值引用只能绑定到左值(具有持久性的对象)。std::forward函数是一个模板函数,用于将参数以原样转发给另一个函数。

使用右值引用完美转发可以避免不必要的对象拷贝和移动,提高代码的效率。同时,它还可以保持参数的值类别,避免了一些潜在的问题,例如在函数模板中传递参数时,如果不使用完美转发,可能会导致参数的值类别发生改变,从而影响函数的行为。

下面是一个使用右值引用完美转发的示例:

#include <iostream>
#include <string>
#include <cstring>
using namespace std;

void bar(int &x)
{
    std::cout << "lvalue: " << x << std::endl;
}

void bar(int &&x)
{
    std::cout << "rvalue: " << x << std::endl;
}

template <typename T, typename... Args>
void foo(Args &&...args)
{
    bar(std::forward<Args>(args)...);
}

int main()
{
    int x = 1;
    foo<int>(x); // lvalue: 1
    foo<int>(2); // rvalue: 2
    return 0;
}

在这个示例中,函数foo使用了右值引用完美转发,将参数args以原样传递给函数bar。bar有两个重载版本,一个接受左值引用,一个接受右值引用,因此可以根据参数的值类别选择正确的版本。在main函数中,分别调用了foo<int>(x)foo<int>(2),分别传递了一个左值和一个右值,输出了对应的结果。

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

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

相关文章

《常规脉搏传输时间作为人体血压变化标志》阅读笔记

目录 一、论文摘要 二、论文十问 Q1: 论文试图解决什么问题&#xff1f; Q2: 这是否是一个新的问题&#xff1f; Q3: 这篇文章要验证一个什么科学假设&#xff1f; Q4: 有哪些相关研究&#xff1f;如何归类&#xff1f;谁是这一课题在领域内值得关注的研究员&#xff1f; …

OpenCV教程——加载、修改、保存图像

1.颜色空间 颜色空间&#xff1a;也称彩色模型&#xff08;又称彩色空间或彩色系统&#xff09;。本质上&#xff0c;彩色模型是坐标系统和子空间的阐述。位于系统的每种颜色都有单个点表示。RGB&#xff08;红绿蓝&#xff09;是依据人眼识别的颜色定义出的空间&#xff0c;可…

「二线豪华」或成历史,理想反超沃尔沃再树「里程碑」

今年的上海车展&#xff0c;除了占据C位的新能源汽车&#xff0c;还有传统车企。 上海车展开幕前&#xff0c;沃尔沃汽车大中华区销售公司总裁钦培吉在新车发布会上直言&#xff1a;“新势力会的&#xff0c;我们三年就学会了&#xff1b;我们会的&#xff0c;新势力十年都学不…

SQL事务与存储引擎

索引回顾&#xff1a; 索引是一个排序的列表&#xff0c;包含字段的值和值所在行数据的物理地址 事务是一个机制&#xff0c;一个操作序列&#xff08;一组操作命令&#xff09;&#xff0c;事务会把所有命令当做一个整体向系统提交或撤销操作&#xff0c;要么都执行&#xf…

MySQL中的Join 的算法(NLJ、BNL、BKA)

本文已收录至Github&#xff0c;推荐阅读 &#x1f449; Java随想录 文章目录 摘要什么是JoinIndex Nested-Loop JoinBlock Nested-Loop JoinMRR & BKA总结 摘要 Join是MySQL中最常见的查询操作之一&#xff0c;用于从多个表中获取数据并将它们组合在一起。Join算法通常使…

【概念大全(关系,码,选择,投影,连接,运算)】第二章 关系数据库

第二章 关系数据库 1. 关系的基本概念1. 什么是域2. 笛卡尔积3. 笛卡尔积中 有意义的子集 就是关系4. 候选码 &#xff08;是唯一标识符 并不是用 只有一个进行判断&#xff09;5. 全码&#xff08;一行中都不重复&#xff09;6. 主码&#xff08;候选码选一个就是主码&#xf…

手术麻醉临床信息系统源码,实时自动采集麻醉和监护设备的数据

手术麻醉临床信息系统源码 手术麻醉临床信息系统实时采集麻醉和监护设备的数据&#xff0c;实现术前、术中、术后全手术过程的数字化管理&#xff0c;为手术室提供全数字化的业务管理、临床管理、费用管理、材料管理等。同时通过与 HIS、EMR、PACS、LIS 等系统无缝集成&#x…

Linux中信号的基础知识

信号的概念 Linux操作系统中&#xff0c;信号是一种进程间通信&#xff08;Inter-Process Communication, IPC&#xff09;机制&#xff0c;用于向其他进程发送通知或指示&#xff0c;通常是为了通知特定事件的发生&#xff0c;如程序终止、用户按下特定按键等。信号提供了一种…

java获取输入内容的方法

Java中的对象类型可以有多种&#xff0c;比如 Object、 StringBuilder等&#xff0c;其中 Object和 String是最常用的对象类型&#xff0c;而 StringBuilder类是一种特殊的类&#xff0c;它能通过继承来创建其他的对象。 我们在平时的工作中经常会遇到需要获取输入内容的情况&a…

界面控件DevExpress Blazor UI v22.2亮点:全新的Window组件

DevExpress拥有.NET开发需要的所有平台控件&#xff0c;包含600多个UI控件、报表平台、DevExpress Dashboard eXpressApp 框架、适用于 Visual Studio的CodeRush等一系列辅助工具&#xff0c;该组件拥有众多新产品和数十个具有高影响力的功能&#xff0c;可为桌面、Web和移动应…

详解async 与 await,带您理解Playwright使用异步方法的正确姿势!

大家在使用python做playwright自动化测试的过程中&#xff0c;一定会发现下面这种异步用法 async def func():await apiawait api 很多同学可能只是按照这种写法来编写项目的自动化测试代码&#xff0c;对于具体细节可能并不了解&#xff0c;今天我就来讲一下playwright异步用…

基于fNIRS的脑功能连接分析:图论方法

导读 背景&#xff1a;fNIRS是一种利用近红外光谱进行功能神经成像的光学脑监测技术。它使用近红外光来测量大脑活动&#xff0c;并估计由于运动活动而引起的大脑皮层血流动力学活动。fNIRS通过光学吸收来测量含氧和脱氧血红蛋白中氧水平的变化。多源噪声和伪影干扰导致的信号…

【P6】JMeter HTTP Cookie管理器

文章目录 一、测试网站二、Cookie 设置规则2.1、无配置元件时&#xff0c;Cookie 不会自动设置&#xff08;与线程组设置无关&#xff09;2.2、有配置元件&#xff0c;不选任何参数时&#xff0c;Cookie 自动设置&#xff08;与线程组设置无关&#xff09;2.3、有配置元件&…

Java——二叉搜索树中第k小的元素

题目链接 leetcode在线oj题——二叉搜索树中第k小的元素 题目描述 给定一个二叉搜索树的根节点 root &#xff0c;和一个整数 k &#xff0c;请你设计一个算法查找其中第 k 个最小元素&#xff08;从 1 开始计数&#xff09;。 题目示例 示例1 输入&#xff1a;root [3,1…

软件工程本科生毕业论文中常见问题总结

文章目录 目录结构不合理 绪论&#xff08;引言&#xff09;研究内容 表格表格首行不要加粗表格能不跨页的就不要跨页 其他常见格式问题专有名词要用统一写法 首先先仔细阅读&#xff1a; 本科生毕业论文&#xff08;设计&#xff09;写作与排版打印规范 目录 结构不合理 2.…

Ubuntu 增加swap交换内存

一、创建虚拟内存 在实际开发中发现swap交换分区不够用了&#xff0c;于是需要创建虚拟内存来增加交换分区的大小。 在系统空闲空间位置创建swap虚拟内存专用文件夹 cd /data //切到你想要创建交换分区的目录 mkdir swap //新建文件夹swap cd swap //进入swap文件夹 备…

Fastjson<1.2.48远程代码执行漏洞(CNVD-2019-22238)

漏洞存在原因 在fastjson<1.2.24版本中&#xff0c;在解析json的过程中&#xff0c;支持使用autoType来实例化某一个具体的类&#xff0c;并调用该类的set/get方法来访问属性。而在1.24<fastjson<1.2.48版本中后增加了反序列化白名单&#xff0c;而在1.2.48以前的版本…

【容器化应用程序设计和开发】2.4 容器网络和存储

往期回顾&#xff1a; 第一章&#xff1a;【云原生概念和技术】 第二章&#xff1a;2.1 容器化基础知识和Docker容器 第二章&#xff1a;2.2 Dockerfile 的编写和最佳实践 第二章&#xff1a;2.3 容器编排和Kubernetes调度 2.4 容器网络和存储 容器网络和存储是容器化应用…

操作系统第二章——进程与线程(下)

东风夜放花千树&#xff0c;更吹落&#xff0c;星如雨 文章目录 2.3.1 进程同步&#xff0c;进程互斥知识总览什么是进程同步什么是进程互斥知识回顾 2.3.2 进程互斥的软件实现方法知识总览如果没有进程互斥单标志法双标志先检查法双标志后检查法Peterson算法知识回顾 2.3.3进程…

Linkage Mapper解密数字世界链接 专栏内容介绍

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: Linkage Mapper解密数字世界链接 在数字时代&#xff0c;链接是信息的核心&#xff0c;链接地…