第6章 右值引用

news2024/9/22 19:25:10

6.1 左值和右值
 

区分左值与右值:

看能不能取地址 & 若能取地址则为左值  不能取地址为右值

int x = 1;
x++;//这个是右值
++x;//左值
++x实现 
int tmp = x;
x = x+1;
return tmp; 返回临时的

主要字符串也是左值 它可以取地址

6.2 左值引用

当我们需要将一个对象作为参数传递给子函数的时候,往往会使用左值引用,因为这样可以免去创建临时对象的操作。非常量左值的引用对象很单纯,它们必须是一个左值。对于这一点,常量左值引用的特性显得更加有趣,它除了能引用左值,还能够引用右值

int &a = 10;//报错
const int& a = 5;//常量引用
class X {
public:
    X() {}
    X(const X&) {}
    X& operator = (const X&) { return *this; }//这里是用在 x3 = make_X    函数得到是右值
};
X make_x()
{
    return X();//这里返回的是右值  需要用常量引用来接收 就是const X&
}
int main()
{
    X x1;
    X x2(x1);
    X x3(make_x());
    x3 = make_x();
}

 6.3 右值引用

常量左值引用可以绑定右值是一条非常棒的特性,但是它也存在一个很大的缺点——常量性。一旦使用了常量左值引用,就表示我们无法在函数内修改该对象的内容 引出右值引用

右值引用是一种引用右值且只能引用右值的方法
 

int&& t = 10;

右值引用的特点之一是可以延长右值的生命周期

# include <iostream>
class X {
public:
    X() { std::cout << "X ctor" << std::endl; }
    X(const X& x) { std::cout << "X copy ctor" << std::endl; }
    ~X() { std::cout << "X dtor" << std::endl; }
    void show() { std::cout << "show X" << std::endl; }
};
X make_x()
{
    X x1;
    cout <<"1111  "<< & x1 << endl;
    return x1;
}
int main()
{
    X&& x2 = make_x();//这里不使用右值引用应该会发生三次构造 make_X中 x1一次return 一次 给x2一次
    //使用右值引用少了最后一次 给最后的临时变量的生命周期加长了
cout << "1111  " << &x2 << endl;
    x2.show();
}

6.4 右值的性能优化空间

#include <iostream>
class BigMemoryPool {
public:
    static const int PoolSize = 4096;
    BigMemoryPool() : pool_(new char[PoolSize]) {
        cout << "1111" << endl;
    }
    ~BigMemoryPool()
    {
        if (pool_ != nullptr) {
            delete[] pool_;
        }
    }
    BigMemoryPool(const BigMemoryPool& other) : pool_(new
        char[PoolSize])
    {
        std::cout << "copy big memory pool." << std::endl;
        memcpy(pool_, other.pool_, PoolSize);
    }
private:
    char* pool_;
};
BigMemoryPool get_pool(const BigMemoryPool& pool)
{
    return pool;
}
BigMemoryPool make_pool()
{
    BigMemoryPool pool;
    return get_pool(pool);
}
int main()
{
    BigMemoryPool my_pool = make_pool();
}

以上代码同样GCC需要加上编译参数-fno-elideconstructors

用VS的好像不能这样

这里会调用三次拷贝构造  get一个 make一个 最后 main函数返回值一个

还有很大的优化空间

6.5 移动语义

class BigMemoryPool {
public:
    static const int PoolSize = 4096;
    BigMemoryPool() : pool_(new char[PoolSize]) {}
    ~BigMemoryPool()
    {
        if (pool_ != nullptr) {
            delete[] pool_;
        }
    }
    BigMemoryPool(BigMemoryPool&& other)
    {
        std::cout << "move big memory pool." << std::endl;
        pool_ = other.pool_;
        other.pool_ = nullptr;
    }
    BigMemoryPool(const BigMemoryPool& other) : pool_(new
        char[PoolSize])
    {
        std::cout << "copy big memory pool." << std::endl;
        memcpy(pool_, other.pool_, PoolSize);
    }
private:
    char* pool_;
};
BigMemoryPool get_pool(const BigMemoryPool& pool)
{
    return pool;
}
BigMemoryPool make_pool()
{
    BigMemoryPool pool;
    return get_pool(pool);
}
int main()
{
    BigMemoryPool my_pool = make_pool();
}

加入移动语句 这样后面两次的拷贝就变成了移动 

6.6 值类别

对于将亡值(xvalue),读者实际上只需要知道它是泛左值和右值交集即可
这里咕噜咕噜一大堆    等后边用到这些再回来看

6.7 将左值转换为右值

int a = 10;
int&& x = a ;//error 我们知道右值引用不能绑定左值 
int&& x = static_cast<int&&>(a); //这样子就行 显式的将左值转为将亡值
它的最大作用是让左值使用移动语义

BigMemoryPool my_pool1;
BigMemoryPool my_pool2 = my_pool1;
BigMemoryPool my_pool3 = static_cast<BigMemoryPool &&>(my_pool1);
//my_pool1是左值 static_cast将它变为将亡值 可以使用移动语句 但是 后续不能对my_pool1操作了

无论一个函数的实参是左值还是右值,其形参都是一个左值,即使这个形参看上去是一个右值引用

void move_pool(BigMemoryPool &&pool)
{
    std::cout << "call move_pool" << std::endl;
    BigMemoryPool my_pool(pool);//这里是左值调用拷贝构造
}
int main()
{
    move_pool(make_pool());
}

要想用移动构造

void move_pool(BigMemoryPool &&pool)
{
    std::cout << "call move_pool" << std::endl;
    BigMemoryPool my_pool( static_cast<BigMemoryPool &&>pool);//这里是右值调用移动构造
}
int main()
{
    move_pool(make_pool());
}

将左值变为右值 还有move函数 内部就是用static_cast 只是它是模板函数 不需要直接定义类型 方便

6.8 万能引用和引用折叠

万能引用长的和右值引用一样 都是&&  若是有类型的推导就是万能引用 若是没有类型推导就是右值引用 

int&& a = 10;//右值引用
auto && x = 10//万能引用 

在模板当中&&含有类型推导都是万能引用 既可以用左值也可以用右值

使用万能引用右引用折叠 就是一堆的&&&这个

所有右值引用折叠到右值引用上仍然是一个右值引用。(A&& && 变成 A&&)
2.所有的其他引用类型之间的折叠都将变成左值引用。 (A& & 变成 A&; A& && 变成 A&; A&& & 变成 A&)

只要有左值引用参与进来,最后推导的结果就是一个左值引用。只有实际类型是一个非引用类型或者右值引用类型时,最后推导出来的才是一个右值引用。
万能引用的形式必须是T&&或者auto&&,也就是说它们必须在初始化的时候被直接推导出来
 

#include <vector>
template<class T>
void foo(std::vector<T>&& t) {}
int main()
{
    std::vector<int> v{ 1,2,3 };
    foo(v); // 编译错误
}

6.9 完美转发
 

与万能引用结合 : 万能引用用途

用于模板当中 用于传递参数 给调用函数 它真正的值类别 

为什么呢 

当我们在模板函数中传递参数时,通常会用泛型 T 来接收传入的参数。但是,如果我们传递的是右值而没有使用完美转发,右值的状态会被“降级”成左值,这意味着移动语义失效,编译器会认为这是一个左值并调用拷贝构造函数,而不是移动构造函数。

#include <iostream>
#include <utility>
#include <string>

// 一个简单的类,用于模拟资源
class MyResource {
public:
    std::string name;
    MyResource(const std::string& n) : name(n) {
        std::cout << "Constructing " << name << std::endl;
    }
    
    MyResource(MyResource&& other) noexcept : name(std::move(other.name)) {
        std::cout << "Move constructing " << name << std::endl;
    }

    MyResource(const MyResource& other) : name(other.name) {
        std::cout << "Copy constructing " << name << std::endl;
    }
};

// 这个函数接受任意类型的参数,并将它完美转发给另一个函数
template<typename T>
void passThrough(T&& arg) {
    std::cout << "passThrough called" << std::endl;
    process(std::forward<T>(arg));  // 完美转发
}

// 用来处理传入的参数
void process(const MyResource& res) {
    std::cout << "Process by const lvalue reference: " << res.name << std::endl;
}

void process(MyResource&& res) {
    std::cout << "Process by rvalue reference: " << res.name << std::endl;
}

int main() {
    MyResource res1("Resource1");          // 创建一个对象
    passThrough(res1);                     // 传入左值,应该调用 lvalue 版本的 process
    passThrough(MyResource("Resource2"));  // 传入右值,应该调用 rvalue 版本的 process
    passThrough(std::move(res1));          // 显式地将 res1 转换为右值
}

6.10 针对局部变量和右值引用的隐式移动操作

c++20的用法略
 

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

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

相关文章

数据结构与算法——Java实现 9.习题——删除链表倒数节点

目录 19. 删除链表的倒数第 N 个结点 方法1 通过链表长度直接删除 方法2 递归加入哨兵节点 ListNode 方法3 快慢指针法 苦难&#xff0c;区区挫折罢了&#xff0c;而我必定站在幸福的塔尖 —— 24.9.22 19. 删除链表的倒数第 N 个结点 给你一个链表&#xff0c;删除链表的倒数第…

【LeetCode】146. LRU缓存

1.题目 2.思想 3.代码 3.1 代码1 下面这是一版错误的代码。错误的原因在于逻辑不正确导致最后的代码也是不正确的。 class LRUCache:def __init__(self, capacity: int):self.time 0 # 用于全局记录访问的时间self.num2time {} # 数字到时间的映射self.key2val {} # 数字…

第十四章:html和css做一个心在跳动,为你而动的表白动画

💖 让心跳加速,传递爱意 💖 在这个特别的时刻,让爱在跳动中绽放!🌟 无论是初次相遇的心动,还是陪伴多年的默契,我们的心总在为彼此跳动。就像这颗炙热的爱心,随着每一次的跳动,传递着满满的温暖与期待。 在这个浪漫的季节,让我们一同感受爱的律动!无论你是在…

Linux文件IO(七)-复制文件描述符

在 Linux 系统中&#xff0c;open 返回得到的文件描述符 fd 可以进行复制&#xff0c;复制成功之后可以得到一个新的文件描述符&#xff0c;使用新的文件描述符和旧的文件描述符都可以对文件进行 IO 操作&#xff0c;复制得到的文件描述符和旧的文件描述符拥有相同的权限&#…

自学笔记之TVM编译器框架 ,核心特性,模型优化概述,AI应用落地

最近在学习一些和芯片 AI相关的知识&#xff0c;重点了解了一下TVM&#xff0c;我自己认为TVM在AI应用落地类似的项目中&#xff0c;用途还是非常广泛的&#xff0c;现在把一些重要的笔记贴在下面&#xff0c;有两篇原帖链接也附上&#xff0c;感兴趣的同学可以学习一下。 TVM…

宝塔linux 安装code-server指定对应的端口无法访问

这个一般就是nginx搞的鬼&#xff0c;如果服务正常启动&#xff0c;就是访问不了&#xff1b;大概就是宝塔安装的nginx配置没有代理code-server服务对应的端口&#xff0c;一般就是nginx配置文件的问题 安装默认的nginx会有一个配置文件 直接拉到最后会有一行这个&#xff0c…

(三十二)栈(stack)

文章目录 栈&#xff08;stack&#xff09;1.1 操作1.2 关于栈的题目1.2.1 出栈顺序1.2.2 入栈顺序1.3.3 括号匹配 这篇文章将会讲两个不同的数据类型&#xff0c;分别是栈与队列&#xff08;双向队列是两个的拓展&#xff09; 栈&#xff08;stack&#xff09; 栈是一种特殊的…

自闭症孩子送寄宿学校,给他们成长的机会

在自闭症儿童的教育与康复之路上&#xff0c;选择一种合适的寄宿方式对于孩子的成长至关重要。这不仅关乎到孩子能否获得专业的训练与关怀&#xff0c;还直接影响到他们未来的社交能力、独立生活能力以及心理健康。今天&#xff0c;我们将以广州的星贝育园自闭症儿童寄宿制学校…

stm32单片机个人学习笔记6(EXTI外部中断)

前言 本篇文章属于stm32单片机&#xff08;以下简称单片机&#xff09;的学习笔记&#xff0c;来源于B站教学视频。下面是这位up主的视频链接。本文为个人学习笔记&#xff0c;只能做参考&#xff0c;细节方面建议观看视频&#xff0c;肯定受益匪浅。 STM32入门教程-2023版 细…

【动态库的加载】【进程地址空间(三)】

目录 1. 宏观看待动态库的加载2. 进程地址空间第二讲2.1 程序没有加载前的地址2.2 程序加载后的地址 3. 动态库的地址 再谈进程地址空间时&#xff0c;【Linux】动静态库 我们先讲一个关于动态库是如何加载的话题&#xff0c;再引入进程地址空间&#xff0c;再次谈论该话题。 …

在ComfyUI中,Cross-Attention优化方案应该选哪个?

&#x1f431;‍&#x1f409;背景 在comfyui中&#xff0c;如果你使用了秋叶的启动器&#xff0c;会在高级选项中看到这样一行选项&#xff1a;Cross-Attention优化方案&#xff0c;右边有个下拉框&#xff0c;可以选择的选项有4个&#xff0c;如下图&#xff1a; 那么&#…

设计模式之观察者模式例题

答案&#xff1a;D 知识点&#xff1a; 观察者模式意图是定义对象间的一种一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖于它的对象都得到通知并被自动更新 状态模式 意图&#xff1a;允许一个对象在其内部状态改变时改变它的行为

【verilog】4. gtkwave的调用

文章目录 前言实验步骤 前言 进行 数电 FPGA 实验 实验步骤 将 GTKwave 的 bin 文件夹路径添加到 “系统环境变量” 的 “Path” 中 启动 debugger wizard, 设置观测信号 编译选择 2进制 文件 点击 start programming connect debugger 选择触发方式 Run 自动打开 gtkwave&a…

[Meachines] [Medium] Querier XLSM宏+MSSQL NTLM哈希窃取(xp_dirtree)+GPP凭据泄露

信息收集 IP AddressOpening Ports10.10.10.125TCP:135, 139, 445, 1433, 5985, 47001, 49664, 49665, 49666, 49667, 49668, 49669, 49670, 49671 $ nmap -p- 10.10.10.125 --min-rate 1000 -sC -sV -Pn PORT STATE SERVICE VERSION 135/tcp open msrp…

【若依RuoYi-Vue | 项目实战】帝可得后台管理系统(二)

文章目录 一、人员管理1、需求说明2、生成基础代码&#xff08;1&#xff09;创建目录菜单&#xff08;2&#xff09;添加数据字典&#xff08;3&#xff09;配置代码生成信息&#xff08;4&#xff09;下载代码并导入项目 3、人员列表改造&#xff08;1&#xff09;基础页面&a…

机器学习算法那些事 | TPAMI 2024.9 | FeatAug-DETR:通过特征增强丰富DETRs的一对多匹配

本文来源公众号“机器学习算法那些事”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;TPAMI 2024.9 | FeatAug-DETR&#xff1a;通过特征增强丰富DETRs的一对多匹配 论文标题&#xff1a;FeatAug-DETR: Enriching One-to-Many Mat…

智能指针:作用 | 使用 | 原理 | 内存泄漏

&#x1f308;个人主页&#xff1a; 南桥几晴秋 &#x1f308;C专栏&#xff1a; 南桥谈C &#x1f308;C语言专栏&#xff1a; C语言学习系列 &#x1f308;Linux学习专栏&#xff1a; 南桥谈Linux &#x1f308;数据结构学习专栏&#xff1a; 数据结构杂谈 &#x1f308;数据…

golang学习笔记4-基本数据类型

注&#xff1a;本人已有C&#xff0c;C,Python基础&#xff0c;只写本人认为的重点。 go的数据类型如下 由于bool和c类似&#xff0c;和go的区别是&#xff0c;bool的值只能取true和false&#xff0c;不能取整数&#xff0c;而且有默认值false。 一、整数型 整数型存放整数&…

设计模式之策略模式例题

答案&#xff1a;A 知识点&#xff1a; 策略模式又叫模板方法模式 它的意图是定义一个操作中的算法骨架。而将一些步骤延迟到子类中&#xff0c;使得子类可以不改变一个算法的结构即可重新定义算法的某些特定步骤

3.5.2 __ipipe_init()之完成中断处理程序设置

点击查看系列文章 》 Interrupt Pipeline系列文章大纲-CSDN博客 原创不易&#xff0c;需要大家多多鼓励&#xff01;您的关注、点赞、收藏就是我的创作动力&#xff01; 3.5.2 __ipipe_init()之完成中断处理程序设置 __ipipe_init()最核心的就是__ipipe_enable_pipeline()&am…