C++复习笔记--完美转发、std::move和std::forward的使用

目录

1--完美转发

1-1--实例代码分析

2--std::forward的使用

2-1--std::forward的源码

2-2--std::forward大白话总结

3--std::move的使用

3-1--std::move的源码

3-2--std::move大白话总结


1--完美转发

为什么需要完美转发

        C++ Primer 关于转发 (P612) 有这么一段话:某些函数需要将其一个或多个实参连同类型不变地转发给其他函数,在此情况下,我们需要保持被转发实参的所有性质

        用大白话来讲,就是希望转发某些参数时,我们不希望这个参数在转发过程中被改变了(例如由右值改变为了左值),而完美转发就可以实现上面的愿景;

1-1--实例代码分析

        结合以下代码讲解实现完美转发的实例:代码参考

#include <iostream>template<typename T>
void print(T & t){ // 只接受左值std::cout << "Lvalue ref" << std::endl;
}template<typename T>
void print(T && t){ // 万能引用,可以接受左值和右值std::cout << "Rvalue ref" << std::endl;
}template<typename T>
void testForward(T && v){ print(v);print(std::forward<T>(v)); print(std::move(v)); std::cout << "======================" << std::endl;
}int main(int argc, char * argv[]){int x = 1;testForward(x); // 传递左值testForward(1); // 传递右值
}

        代码运行结果如下:

Lvalue ref
Lvalue ref
Rvalue ref
======================
Lvalue ref
Rvalue ref
Rvalue ref
======================

代码分析

对于第一个 testForward(x) 的执行:

        ① x 本身是一个左值,当传递给函数 testForward(T && v) 时,T 会被推断为 int &(推断分析参考笔记:万能引用和引用折叠);函数模板等价于 testForward(int & v);(发生了引用折叠 T && → int & && → int &);执行第一个 print(v) 时,v是一个左值,因此优先调用第一个 print() 函数;

        ② 执行第二个 print(std::forward<T>(v)) 时,v 是一个左值,经过 std::forward<T>(v) 返回的值仍然是一个左值(后面会结合源码分析 std::forward 的使用),因此优先调用第一个 print() 函数;

        ③ 执行第三个 print(std::move(v)) 时,v 是一个左值,经过 std::move(v) 返回的值是一个右值(后面会结合源码分析 std::move 的使用),因此只能调用第二个 print() 函数;

对于第二个 testForward(1) 的执行:

        ① 字面值 1 本身是一个右值,当传递给函数 testForward(T && v) 时,T 会被推断为 int;函数模板等价于 testForward(int  v); 执行第一个 print(v) 时,由于 v 这时用一个变量拥有地址)来存储了数值 1,因此 v 是一个左值,会优先调用第一个 print() 函数;

        ② 执行第二个 print(std::forward<T>(v)) 时,v 是一个左值,由于 T 被推断为 int,则经过 std::forward<T>(v) 返回的值是一个右值(会结合源码分析),因此只能调用第二个 print() 函数;

        ③ 执行第三个 print(std::move(v)) 时,v 是一个左值,经过 std::move(v) 返回的值是一个右值(会结合源码分析),因此只能调用第二个 print() 函数;

        上面实例是为了说明 std::forward<T>() 函数能够根据 T 的不同,返回值的同时,能够很好地维持本来该有的类型属性;以下会结合源码分析 std::forward 和 std::move 的作用;

2--std::forward的使用

2-1--std::forward的源码

/***  @brief  Forward an lvalue.*  @return The parameter cast to the specified type.**  This function is used to implement "perfect forwarding".*/template<typename _Tp>_GLIBCXX_NODISCARDconstexpr _Tp&&forward(typename std::remove_reference<_Tp>::type& __t) noexcept{ return static_cast<_Tp&&>(__t); }/***  @brief  Forward an rvalue.*  @return The parameter cast to the specified type.**  This function is used to implement "perfect forwarding".*/template<typename _Tp>_GLIBCXX_NODISCARDconstexpr _Tp&&forward(typename std::remove_reference<_Tp>::type&& __t) noexcept{static_assert(!std::is_lvalue_reference<_Tp>::value,"std::forward must not be used to convert an rvalue to an lvalue");return static_cast<_Tp&&>(__t);}

简单结合第一个模板的源码来分析上面的代码实例:

        ① 对于 std::forward<T>(v),当 v 是一个左值,且 T 已经被推断为 int & 时的情况,std::forward<T>(v) 等价于 std::forward<int&>(v);即对于以下的源码,传入的 _Tp 是 int&

  template<typename _Tp>_GLIBCXX_NODISCARDconstexpr _Tp&&forward(typename std::remove_reference<_Tp>::type& __t) noexcept{ return static_cast<_Tp&&>(__t); }

        对于上面的源码,首先需知道 std::remove_reference<int&>::type 的结果是 int(具体可见C++ Primer Page 605),_Tp&&发生引用折叠_Tp&& → int& && → int&,则源码等价于 constexpr int& forward(int& __t)返回的类型int&,即返回的类型对应左值引用,则返回的值是一个左值

        ② 对于 std::forward<T>(v),当 v 是一个左值,且 T 已经被推断为 int 时的情况,

std::forward<T>(v) 等价于 std::forward<int>(v);即对于上面的源码,传入的 _Tp 是 int;std::remove_reference<int>::type 的结果仍然是 int,则源码等价于 constexpr int&& forward(int& __t)返回的类型int&&,即返回的类型对应右值引用,则返回的值是一个右值

        (这里可能会有疑惑?明明返回的类型是右值引用,右值引用虽然只能绑定右值,但其本质上是一个左值呀,为什么返回的是右值呀?)(回答:首先返回类型和返回的值是不同的,std::forward这里返回的是值,这个值并没有用一个变量去存储,而是直接返回了,因此这个返回的值是右值,不能调用上面实例的第一个print()函数,尽管这个返回的值的类型是右值引用int &&

// 对于下面的代码, auto是被推断为 int&& 的,可以调用上面实例中的第一个print函数
// 因为这时用一个变量a来接收返回的右值,a就变成了左值,左值可以调用print()函数
auto a = std::forward<int>(v);
#include <iostream>template<typename T>
void print(T & t){ // 只接受左值std::cout << "Lvalue ref" << std::endl;
}int main(int argc, char * argv[]){int v = 1;auto a = std::forward<int>(v); // int &&a = 1; // a 变成了左值print(a);
}

2-2--std::forward大白话总结

        当传入一个实参到 std::forward 中,其显式类型是T,则返回的值与实参的值相同,且返回类型T&&

3--std::move的使用

3-1--std::move的源码

/***  @brief  Convert a value to an rvalue.*  @param  __t  A thing of arbitrary type.*  @return The parameter cast to an rvalue-reference to allow moving it.*/template<typename _Tp>_GLIBCXX_NODISCARDconstexpr typename std::remove_reference<_Tp>::type&&move(_Tp&& __t) noexcept{ return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }

简单结合模板的源码来分析上面的代码实例:

        ① 对于std::move(v) 时,v 是一个左值,v 的类型是 int &,当传入 std::move(v) 函数时,_Tp推断为int &,源码等价于 int&& move(int& _t); 接收一个左值,返回类型是 int&&,即右值引用类型,返回的值是一个右值,因此只能调用上面代码实例中的第二个 print() 函数;

        ② 对于std::move(v) 时,v 是一个左值,v 的类型是 int,当传入 std::move(v) 函数时,_Tp推断为int,源码等价于 int&& move(int&& _t); 接收一个右值,返回类型是 int&&,即右值引用类型,返回的值是一个右值,因此也只能调用上面代码实例中的第二个 print() 函数;

3-2--std::move大白话总结

        std::move()无论传入的是一个左值还是一个右值,返回的都是一个右值,其类型是右值引用类型

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

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

相关文章

【C++】红黑树

目录 一、红黑树的概念二、红黑树的性质三、红黑树的插入操作四、红黑树的验证五、红黑树和AVL树的比较六、代码 一、红黑树的概念 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是Red或Black。 通过对任何一条从…

winform通过消息机制触发事件

在 WinForms 应用程序中&#xff0c;你可以通过使用消息机制来触发自定义事件。这可以通过 WndProc 方法来实现&#xff0c;该方法用于处理窗口消息。以下是一个简单的示例&#xff0c;演示如何通过消息机制在 WinForms 中触发自定义事件&#xff1a;首先&#xff0c;创建一个自…

设计模式(9)建造者模式

一、 1、概念&#xff1a;将一个复杂对象的构造与它的表示分离&#xff0c;使得同样的构造过程可以创建不同的表示。建造者模式主要用于创建一些复杂的对象&#xff0c;这些对象内部构建间的顺序通常是稳定的&#xff0c;但对象内部的构建通常面临着复杂的变化&#xff1b;建造…

[SpringBoot3]Web服务

五、Web服务 基于浏览器的B/S结构应用十分流行。SpringBoot非常适合Web应用开发&#xff0c;可以使用嵌入式Tomcat、Jetty、Undertow或Netty创建一个自包含的HTTP服务器。一个SpringBoot的Web应用能够自己独立运行&#xff0c;不依赖需要安装的Tomcat、Jetty等。SpringBoot可以…

indexDB入门到精通

前言 由于开发3D可视化项目经常用到模型&#xff0c;而一个模型通常是几m甚至是几十m的大小对于一般的服务器来讲加载速度真的十分的慢&#xff0c;为了解决这个加载速度的问题&#xff0c;我想到了几个本地存储的。 首先是cookie,cookie肯定是不行的&#xff0c;因为最多以只…

前端面试:【事件处理】探索事件流、委托与事件对象

嗨&#xff0c;亲爱的事件探险家&#xff01;在JavaScript的世界中&#xff0c;事件处理是与用户互动的关键。本文将带你探索事件流、事件委托、常见事件类型和事件对象&#xff0c;这些知识将帮助你成为事件处理的大师。 2. 事件流&#xff1a;事件的旅程 事件流描述了事件从…

Vue的Ajax请求-axios、前后端分离练习

Vue的Ajax请求 axios简介 ​ Axios&#xff0c;是Web数据交互方式&#xff0c;是一个基于promise [5]的网络请求库&#xff0c;作用于node.js和浏览器中&#xff0c;它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生node.js http模块, 而在…

SpringBoot +Vue3 简单的前后端交互

前端&#xff1a;Vue3 创建项目&#xff1a; npm create vuelatest > cd <your-project-name> > npm install > npm run dev 项目结构图如下&#xff1a; 1、查看入口文件内容&#xff1a;main.js 代码如下&#xff1a; import ./assets/main.css impor…

自己实现 SpringMVC 底层机制 系列之-实现任务阶段 6-完成控制器方法获取参数-@RequestParam

&#x1f600;前言 自己实现 SpringMVC 底层机制 系列之-实现任务阶段 6-完成控制器方法获取参数-RequestParam &#x1f3e0;个人主页&#xff1a;尘觉主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是尘觉&#xff0c;希望我的文章可以帮助到大家&#xff0c…

java基础复习(第七日)

java基础复习(七) 1.MQ如何避免消息重复投递或重复消费&#xff1f; 在消息生产时&#xff0c;MQ 内部针对每条生产者发送到消息生成一个 inner-msg-id&#xff0c;作为去重的依据&#xff08;消息投递失败并重传&#xff09;&#xff0c;避免重复的消息进入队列&#xff1b;…

c++——函数重载

函数重载 1、函数重载的概念 同一作用域内&#xff0c;同一函数名定义多个函数&#xff0c;这些函数的参数个数、参数类型不同、顺序。这就是函数的重载(function overloading)。即对一个函数名重新赋予它新的含义&#xff0c;使一个函数名可以多用。函数重载允许我们使用相同…

攻防世界-Web_php_include

原题 解题思路 php://被替换了&#xff0c;但是只做了一次比对&#xff0c;改大小写就可以绕过。 用burp抓包&#xff0c;看看有哪些文件 flag明显在第一个PHP文件里&#xff0c;直接看

飞天使-k8s基础组件分析-pod

文章目录 pod介绍pod 生命周期init 容器容器handlerpod中容器共享进程空间sidecar 容器共享 参考链接 pod介绍 最小的容器单元 为啥需要pod? 答: 多个进程丢一个容器里&#xff0c;会因为容器里个别进程出问题而出现蝴蝶效应&#xff0c;pod 是更高级的处理方式pod 如何共享相…

【李群李代数】李群控制器(lie-group-controllers)介绍——控制 SO(3) 空间中的系统的比例控制器Demo...

李群控制器SO(3)测试 测试代码是一个用于控制 SO(3) 空间中的系统的比例控制器。它通过计算控制策略来使当前状态逼近期望状态。该控制器使用比例增益 kp 进行参数化&#xff0c;然后进行一系列迭代以更新系统状态&#xff0c;最终检查状态误差是否小于给定的阈值。这个控制器用…

Python 网页解析高级篇:深度掌握BeautifulSoup库

在Python的网络爬虫中&#xff0c;BeautifulSoup库是一个强大的工具&#xff0c;用于解析HTML和XML文档并提取其中的数据。在前两篇文章中&#xff0c;我们已经讨论了BeautifulSoup库的基本和中级使用方法&#xff0c;但BeautifulSoup的能力远远超出了这些。在这篇文章中&#…

VUE Table表格动态列,数据错位问题

1.首先&#xff0c;检查了前端用于接数据的字段是否与后端传过来的字段相同&#xff0c;在确定传参没有出现问题之后&#xff0c;这个问题仍然存在。 2.发生原因&#xff1a;因为使用了elementui&#xff0c;表格通过循环产生&#xff0c;vue在dom重新渲染时存在一个性能优化机…

摩托车外廓尺寸检测软件

本系统为摩托车外廓尺寸检测软件&#xff0c;该系统共涉及两种测量方法&#xff1a;自动测量和手动测量&#xff0c;旨在测量出每一台摩托车的外廓尺寸&#xff0c;包括但不限于摩托车的车长、车宽、车高、轮距、前悬、后悬、前伸距等需要测量的参数&#xff0c;可通过运行软件…

二、Kafka快速入门

目录 2.1 安装部署1、【单机部署】2、【集群部署】 2.2 Kafka命令行操作1、查看topic相关命令参数2、查看当前kafka服务器中的所有Topic3、创建 first topic4、查看 first 主题的详情5、修改分区数&#xff08;注意&#xff1a;分区数只能增加&#xff0c;不能减少&#xff09;…

回归预测 | MATLAB实现WOA-SVM鲸鱼算法优化支持向量机多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现WOA-SVM鲸鱼算法优化支持向量机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现WOA-SVM鲸鱼算法优化支持向量机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09;效果一览基本介绍程…

【C++】早绑定、析构与多态 | 一道关于多态的选择题记录

今天在和群友聊天的时候看到了一道很坑的题目&#xff0c;分享给大家 1.看题&#xff01; 先来看看题目 struct Dad { public:Dad(){ echo();}~Dad(){ echo();}virtual void echo() {cout << "DAD ";} };struct Son:Dad { public:void echo() const override…