C++高级特性:万能引用、引用折叠与完美转发(七)

1、万能引用与引用折叠
1.1、普通引用
  • 之前的学习中学习了左值、右值、左值引用、右值引用、常引用等,但是很可惜它们都必须搭配固定的类型导致它们受到一些限制

    void test1()
    {int a = 1, b = 2;int&  left_ref_var = a;
    //    int&& right_ref_var = a;              // 报错, 右值引用只能引用右值//    int& left_ref_val = 1;                // 报错, 左值引用只能引用左值int&&  right_ref_val = 1;               int& left_ref_right = right_ref_val;    // 左值引用   引用   右值引用(右值引用本质也是左值)const int& const_left_ref_val = 1;      // 常量左值引用, 可以引用右值const int& const_left_ref_var = a;      // 常量左值引用, 也可以引用左值
    //    const_left_ref_val = 2;               // 报错
    //    const_left_ref_var = b;               // 报错const int* const const_ptr_const = &a;  // 常量左值引用的本质
    }
    
    • 上面这些例子很清楚的解释了上面的这些关键字的意思。
    • 左值引用和右值引用都不够万能,而常量左值引用呢又不能修改,这就很头疼。
    • 首先需要明确的一点:右值引用的虽然只能引用右值,但是它也是一个左值,因为它有地址所以是一个左值,因此左值引用可以引用右值引用(左值)
1.2、万能引用
  • 而常量引用又不能修改,C++11中就开始提供一个万能引用的东西,跟&&一样,但是需要看它使用在什么地方。

    void foo(int&& i) { }           // i为右值引用, 是一个左值
    template<class T>
    void bar(T&& t){    }           // t是一个万能引用
    int get_val(){  return 5;}void test2()
    {int&& x = get_val();            // x为右值引用auto&& y1 = 1;    // y为万能引用int a = 5;auto&& y2 = a;	std::cout << "y2 = " << y2 << ", &y2 = " << &y2 << std::endl;std::cout << " a = " << a << ",  &a = " << &a << std::endl;a = 10;std::cout << "y2 = " << y2 << ", &y2 = " << &y2 << std::endl;std::cout << " a = " << a << ",  &a = " << &a << std::endl;y2 = 123;std::cout << "y2 = " << y2 << ", &y2 = " << &y2 << std::endl;std::cout << " a = " << a << ",  &a = " << &a << std::endl;
    }
    

    在这里插入图片描述

万能引用:无论传入什么值还是引用都能够接收,并且确定其一个准确的类型,通过引用折叠的方式。

  • 初始化的源对象如果是一个左值,则目标对象会推导出左值引用。
  • 初始化的源对象如果是一个右值,则目标对象会推导出右值引用。
1.3、引用折叠

由于出现了万能引用,现在传入参数就会变得很复杂,一会传入左值一会传入右值,通过模版参数接手的时候,实际上编译器呢会进行引用折叠的一个操作

模板类型T实际类型R最终推导类型
T&RR&
T&R&R&
T&R&&R&
T&&RR&&
T&&R&R&
T&&R&&R&&
  • 上面的表格中显示了引用折叠的推导规则,可以看出在整个推导过程中只要有左值引用参与进来,最后推倒的结果就是一个左值引用
  • 其实最难理解的是第三条:模板是T的左值引用,遇到R&&右值引用,推导的结果是一个左值引用。其原因是因为右值引用的本质也是一个左值,等价于了第一条…
  • 如果模板是一个右值引用,那么凑满3个&减去2个&就是最终的类型
2、完美转发

C++11中完美转发std::forward在万能引用的基础在进行优化,对于一个对象如果传入什么类型就按照什么类型进行转发。

举个例子:小明有一本书,但是小明不看,我特别想看,那么我有两种获取的办法:

  • 拷贝构造:我去找小明把书接过来复印一份,然后再阅读(这就叫拷贝构造)

  • 移动构造:我直接找小明把书拿过来,反正小明也不看。(这就叫移动构造)

  • 但是现在我不确定小明是借给我复印(拷贝)还是直接借给我看(移动),这需要去问小明,然后小明怎么说我就这么做!

  • 完美转发:我按照小明给我的方式(拷贝or移动)使用,这就叫完美转发!

2.1、完美转发的引入
  • 给定下面一个这样的例子:

    • 一个左值引用的func_push、一个右值引用的func_push函数,二者是重载的关系

    • 然后定义一个模板函数func(T&& t),给定万能引用T&& t类型,内部调用func_push函数

    • 最后在外面测试传入效果

    std::vector<std::string> v;
    void func_push(const std::string& str)
    {std::cout << "(const std::string& str)" << std::endl;v.push_back(str);}
    void func_push(std::string&& str)
    {std::cout << "(std::string&& str)" << std::endl;v.push_back(std::move(str));
    }
    template<class T>
    void func(T&& t)
    {func_push(t);
    }void test3()
    {std::string s = "123";func(s);func(std::move(s));         //std::move(s)是一个亡值表达式, 返回的是一个右值传入给func函数
    }
    /*		输出
    (const std::string& str)
    (const std::string& str)
    */
    
  • 首先分析一下输出结果:

    • func(s):s是一个左值字符串,对于万能引用来说&& + & = &,因此引用折叠推导出是一个左值引用,调用左值引用的func_push
    • func(std::move(s)):而这个东西就很有意思了,它也调用左值引用
      • std::move(s):这是一个亡值表达式,意思是移交s的所有权给func(T&& t),也就是说它是右值
      • 此时对于万能引用来说&& + && ==> &&,引用折叠后推导出是一个右值引用,T&& t = std::move(s)。
      • 而根据上面结论:虽然t现在是一个右值引用,但是t也是一个左值的本质!因此还是会调用左值引用的func_push
  • 那现在的结果就跟我们的需求背道而驰了啊,希望func(std::move(s))调用右值引用的func_push

  • 而如果把func中的func_push(t);改为 ==> func_push(std::move(t));,那么结果又都调用了右值引用的func_push

    template<class T>
    void func(T&& t)
    {
    //    func_push(t);									// func_push(std::move(t));
    }void test3()
    {std::string s = "123";func(s);func(std::move(s));         
    }
    /*		输出
    (std::string&& str)
    (std::string&& str)
    */
    

这两份代码无论如何改,再不引入完美转发和类型转换之后是无法满足需求的。

2.2、完美转发
template<class T>
void func1(T&& t)
{
//    func_push(static_cast<T&&>(t));func_push(std::forward<T>(t));
}
void test4()
{std::string s = "123";func1(s);                               // 输出: (const std::string& str)func1(std::move(s));                    // 输出: (std::string&& str)std::string t;func1(std::move(t));                    // 输出: (std::string&& str)
}
  • forward:按照给定的参数的具体类型进行原样转发,不修改其左右值属性。

  • 输出结果分析:

    • func1(s);:s是一个左值,传给函数模板func1匹配的进行引用折叠,得到一个左值引用,左值引用本身也是一个左值,完美转发时按照左值进行转发。
    • func1(std::move(s)):这里使用了移动语义,std::move(s)是一个亡值表达式,传入右值,经过引用折叠之后得到一个右值引用。此时重头戏来了:因为上面提到右值引用其实也是一个左值,但是使用完美转发后右值引用会按照右值转发!
    • func1(std::move(t)):同上
  • 原理解析:

    std::forward完美转发的源码就下面这些

    template<typename _Tp>
    constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type& __t) noexcept
    { return static_cast<_Tp&&>(__t);
    }
    template<typename _Tp>
    constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
    {static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument substituting _Tp is an lvalue reference type");return static_cast<_Tp&&>(__t);
    }
    template<typename _Tp>
    struct remove_reference
    { typedef _Tp   type; 
    };template<typename _Tp>
    struct remove_reference<_Tp&>
    { typedef _Tp   type; 
    };template<typename _Tp>
    struct remove_reference<_Tp&&>
    { typedef _Tp   type; 
    };
    
    • 首先会调用std::remove_reference进行引用移除,如论是T、T&、T&&类型都最后返回T类型
    • 然后会匹配到对应的不同forward的模板函数进行引用折叠,引用折叠完毕之后右值引用就是返回右值、左值引用就是返回左值。
    • 其实我们可以看到核心就是static<T &&> (__t),我们也可以通过手动类型转化进行"完美转发"…

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

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

相关文章

[MAUI]集成富文本编辑器Editor.js至.NET MAUI Blazor项目

文章目录 获取资源从源码构建从CDN获取获取扩展插件 创建项目创建控件创建Blazor组件初始化保存销毁编写渲染逻辑 实现只读/编辑功能切换模式获取只读模式状态响应切换事件 实现明/暗主题切换项目地址 Editor.js 是一个基于 Web 的所见即所得富文本编辑器&#xff0c;它由CodeX…

自定义滚动条样式:前端实现跨浏览器兼容

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

【核心完整复现】基于目标级联法的微网群多主体分布式优化调度

1 主要内容 之前发布了华电学报的复现程序《基于目标级联法的微网群多主体分布式优化调度》&#xff0c;具体链接为【防骗版】基于目标级联法的微网群多主体分布式优化调度&#xff0c;虽然对模型及结果进行了复现&#xff0c;但是部分模型细节和参数并没有完全实现&#xff0…

MySQL单行函数

文章目录 数值函数基本函数角度与弧度互换函数三角函数指数与对数进制间的转换 字符串函数日期和时间函数获取日期、时间日期与时间戳的转换获取月份、星期、星期数、天数等函数日期的操作函数时间和秒钟转换的函数计算日期和时间的函数日期的格式化与解析 流程控制函数加密与解…

DonkeyDocker-v1-0渗透思路

MY_BLOG https://xyaxxya.github.io/2024/04/13/DonkeyDocker-v1-0%E6%B8%97%E9%80%8F%E6%80%9D%E8%B7%AF/ date: 2024-04-13 19:15:10 tags: 内网渗透Dockerfile categories: 内网渗透vulnhub 靶机下载地址 https://www.vulnhub.com/entry/donkeydocker-1,189/ 靶机IP&a…

elasticSearch从零整合springboot项目实操

type会被弃用 &#xff0c;就是说之后的elasticSearch中只会存在 索引&#xff08;indices&#xff09; 和 一行&#xff08;document&#xff09; 和字段&#xff08;fields&#xff09; elasticSearch 和solr的区别最大的就是 es对应的 是 json的格式 。 solr有xml和josn等…

React Hooks 全解: 常用 Hooks 及使用场景详解

React Hooks 是 React 16.8 版本引入的一项重要特性,它极大地简化和优化了函数组件的开发过程。 React 中常用的 10 个 Hooks,包括 useState、useEffect、useContext、useReducer、useCallback、useMemo、useRef、useLayoutEffect、useImperativeHandle 和 useDebugValue。这些…

ARM内核的CPU架构模型

1.引言 程序员在编码的时候&#xff0c;如果想有进一步的提升&#xff0c;我认为还是要深入底层理解程序运行原理才好。最近看了一些ARM架构的讲解&#xff0c;总结了如下。虽然是以ARM为原型画的图形&#xff0c;但是对于C和C的编程&#xff0c;还是有一些参考价值的。 2. AR…

云服务器租用一年、1个月优惠价格表,阿里/腾讯/京东/华为云

现在租一个服务器多少一个月&#xff1f;优惠价格低至3.8元1个月&#xff0c;租用一个月云服务器收费价格表&#xff1a;阿里云和腾讯云2核2G3M服务器优惠价格61元一年&#xff0c;折合一个月5元&#xff0c;京东云轻量云主机5.8元一个月&#xff0c;华为云服务器优惠价格3.8元…

wangeditor与deaftjs的停止维护,2024编辑器该如何做技术选型(一)

wangeditor暂停维护的声明&#xff1a; wangeditor是国内开发者开发的编辑器&#xff0c;用户也挺多&#xff0c;但是由于作者时间关系&#xff0c;暂停维护。 deaft的弃坑的声明&#xff1a; draft是Facebook开源的&#xff0c;但是也弃坑了&#xff0c;说明设计的时候存在很大…

为数据穿上安全的外衣——零售电商场景下的数据安全体系建设

在电子商务交易过程中&#xff0c;会涉及大量的个人和财务数据的传输和处理&#xff0c;随着电子商务的发展&#xff0c;数据安全风险也成为一个备受关注的问题。 而跨境电商&#xff0c;属于出海业务&#xff0c;涉及到海外不同国家的政策法规&#xff0c;且数据作为电商的业…

等保测评2.0——网络安全等级保护测评的初步了解

一、什么是网络安全等级保护测评&#xff1f; 二、网络安全等级保护&#xff0c;保护的是什么&#xff1f; 等级保护对象&#xff1a;网络安全等级保护工作直接作用的对象。&#xff08;注&#xff1a;主要包括信息系统、通信网络设施和数据资源等&#xff09; 计算机信息系统…

数据库系统概论(超详解!!!)第四节 数据库安全性

问题的提出&#xff1a; 数据库的一大特点是数据可以共享 数据共享必然带来数据库的安全性问题 数据库系统中的数据共享不能是无条件的共享。 1.数据库的安全概述 数据库的安全性是指保护数据库以防止不合法使用所造成的数据泄露、更改或破坏 。 系统安全保护措施是否有效…

Linux【实战篇】—— NFS服务搭建与配置

目录 一、介绍 1.1什么是NFS&#xff1f; 1.2客户端与服务端之间的NFS如何进行数据传输&#xff1f; 1.3RPC和NFS的启动顺序 1.4NFS服务 系统守护进程 二、安装NFS服务端 2.1安装NFS服务 2.2 创建共享目录 2.3创建共享目录首页文件 2.4关闭防火墙 2.5启动NFS服务 2.…

基于VC709构建FPGA开发学习平台(一)-系统时钟与LED

一、时钟 1. 系统时钟 板载U34可编程晶振SI570&#xff0c;为用户提供基础时钟&#xff0c;上电后&#xff0c;默认时钟频率为156.25Mhz&#xff0c;可作为free run时钟源&#xff0c;输入到PLL中&#xff0c;生成系统时钟&#xff1a; 约束为&#xff1a; set_property PACK…

Unity给地图物体添加对撞机

在项目/Assets下创建Prefabs文件夹 选择素材拖入层级下&#xff0c;注意此时地图素材有可能看不到&#xff0c;此时选择Tilemap在检查器中修改图层顺序调至最低。 添加对撞机 选择素材&#xff0c;在检查器中点击添加组件Box Collider 2D&#xff0c;将素材拖入Prefabs文件下…

SSL、TLS和HTTPS:网络安全的重要基石

随着互联网的快速发展&#xff0c;网络安全问题日益凸显。为了保护数据在传输过程中的安全&#xff0c;各种加密协议和技术应运而生。SSL&#xff08;安全套接层&#xff09;、TLS&#xff08;传输层安全&#xff09;和HTTPS&#xff08;超文本传输安全协议&#xff09;是三个最…

力扣LeetCode138. 复制带随机指针的链表 两种解法(C语言实现)

目录 题目链接 题目分析 题目定位&#xff1a; 解题思路 解题思路1&#xff08;粗暴但是复杂度高&#xff09; 解题思路2&#xff08;巧妙并且复杂度低&#xff09; 题目链接 138. 复制带随机指针的链表https://leetcode-cn.com/problems/copy-list-with-random-pointer/ …

【STL详解 —— list的介绍及使用】

STL详解 —— list的介绍及使用 list的介绍list的介绍使用list的构造list iterator的使用list capacitylist element accesslist modifiers 示例list的迭代器失效 list的介绍 list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭…

分类预测 | Matlab实现基于迁移学习和GASF-CNN-Mutilhead-Attention格拉姆角场和卷积网络多头注意力机制多特征分类预测/故障识别

分类预测 | Matlab实现基于迁移学习和GASF-CNN-Mutilhead-Attention格拉姆角场和卷积网络多头注意力机制多特征分类预测/故障识别 目录 分类预测 | Matlab实现基于迁移学习和GASF-CNN-Mutilhead-Attention格拉姆角场和卷积网络多头注意力机制多特征分类预测/故障识别分类效果基…