C++ 新特性 | C++ 11 | std::forward、万能引用与完美转发

文章目录

  • 一、std::forward、万能引用与完美转发
    • 1、万能引用
    • 2、类型推导
    • 3、引用折叠
    • 4、std::forward

概述

std::forward是C++11中引入的一个函数模板,用于实现完美转发。它的作用是根据传入的参数,决定将参数以左值引用还是右值引用的方式进行转发。

传统上,当一个左值传递给一个函数时,参数会以左值引用的方式进行传递;当一个右值传递给一个函数时,参数会以右值引用的方式进行传递。完美转发是为了解决传递参数时的临时对象(右值)被强制转换为左值的问题。std::forward实现完美转发主要用于以下场景:提高模板函数参数传递过程的转发效率

一、std::forward、万能引用与完美转发

1、万能引用

对于形如T&&的变量或者参数,如果T可以进行推导,那么T&&称之为万能引用。换句话说,对于形如T&&的类型来说,其既可以绑定左值,又可以绑定右值,而这个的前提是T需要进行推导。最常见的万能引用方式如以下两种:

函数模板:

template<typename T>
void f(T&& param); // 存在类型推导,param是一个万能引用

auto类型推导:

auto&& var = var1; // 存在类型推导,var是一个万能引用

注意:只有当发生自动类型推断时(例如:函数模板的类型自动推导),T &&才是万能引用。下面一个示例中的&& 并不是一个万能引用,例如:

template<typename T>
void f( T&& param); // 这里T的类型需要推导,所以&&是一个 universal referencestemplate<typename T>
class Test {Test(Test&& rhs);  // Test是一个特定的类型,不需要类型推导,所以&&表示右值引用  
};

2、类型推导

万能引用进行类型推导时需要推导出T&&中的T的真实类型:若传入的参数是一个左值,则T会被推导为左值引用;而如果传入的参数是一个右值,则T会被推导为原生类型(非引用类型)。

对于万能引用来说,条件之一就是类型推导,但是类型推导是万能引用的必要非充分条件,也就是说参数必须被声明为T&&形式不一定是万能引用。示例如下:

template<typename T>
void func(std::vector<T>&& t); // t是右值引用

调用func时会执行类型推导,但是参数t的类型声明的形式并非T &&而是std::vector &&。 之前强调过,万能引用必须是T &&才行,因此,t是一个右值引用,如果尝试将左值传入,编译器将会报错:

std::vector<int> v;
fun(v); // 编译错误,不能将左值绑定到右值

形如const T&&的方式也不是万能引用:

template<typename T>
void f(const T&& t); // t是右值引用int main() {int a = 0;f(a); // 错误
}

3、引用折叠

引用折叠是一种特性,允许在模板元编程中使用引用类型的参数来创建新的引用类型。由于存在T&&这种万能引用类型,当它作为参数时,有可能被一个左值/左值引用或右值/右值引用的参数初始化,这需要通过类型推导,推导后得到的参数类型会发生类型变化,这种变化就称为引用折叠。

根本原因是因为C++中禁止reference to reference,所以编译器需要对四种情况(& && &&&& &,&& &&)进行处理,将他们折叠成一种单一的reference。引用折叠的规则如下:如果两个引用中至少其中一个引用是左值引用,那么折叠结果就是左值引用;否则折叠结果就是右值引用。示例如下:

using T = int &;
T& r1;  // int& & r1 -> int& r1
T&& r2; // int& && r2 -> int& r2using U = int &&;
U& r3;  // int&& & r3 -> int& r3
U&& r4; // int&& && r4 -> int&& r4

下面是一个具体的示例,可以看下对应的推导过程

template<typename T>
void func(T &&t) {cout << "hello world" << endl;
}int main() {int a = 1;int &b = a;func(a); // T 推导成 int &; T && ==> int & && ==> int &func(b); // T 推导成 int &; T && ==> int & && ==> int &func(1); // T 推导成 int; T && ==> int &&func(std::move(a)); // T 推导成 int &&; T && ==> int && && ==> int &&return 0;
}

4、std::forward

完美转发是为了解决传递参数时的临时对象(右值)被强制转换为左值的问题,std::forward源码如下:

template<class T>
T&& forward(typename std::remove_reference<T>::type& t) noexcept {return static_cast<T&&>(t);
}template <class T>
T&& forward(typename std::remove_reference<T>::type&& t) noexcept {return static_cast<T&&>(t);
}

其内部实现只有一行代码,即static_cast<T&&>(t)使用static_cast<>进行类型转换,与std::move()实现方式类似。结合前面介绍的引用折叠,当接收一个左值作为参数时,std::forward<>()返回左值引用,相应的,当接收一个右值作为参数时,std::forward<>()返回右值引用。

版本一:没有实现完美转发

下面给出一个案例没有实现完美转发,如下:

#include <iostream>template <typename T>
void wrapper(T u) {fun(u);
}class MyClass {};void fun(MyClass& a) { std::cout << "in fun(MyClass&)\n"; }
void fun(const MyClass& a) { std::cout << "in fun(const MyClass&)\n"; }
void fun(MyClass&& a) { std::cout << "in fun(MyClass &&)\n"; }int main(void) {MyClass a;const MyClass b;fun(a);fun(b);fun(MyClass());std::cout << "----- Wrapper ------\n";wrapper(a);wrapper(b);wrapper(MyClass());return 0;
}

输出结果:

in func(MyClass&)
in func(const MyClass&)
in func(MyClass &&)
----- Wrapper ------
in func(MyClass&)
in func(const MyClass&)
in func(const MyClass&)Process returned 0 (0x0)   execution time : 0.253 s
Press any key to continue.

最后一行函数调用结果不符合预期,传入的是MyClass &&右值引用,预期调用fun(MyClass&& a),实际上调用的却是fun(const MyClass& a)。调用wrapper函数时触发拷贝构造,基于右值创建了左值u(即:wrapper函数的参数),u的实际类型是const MyClass,匹配的是fun(const MyClass& a)

版本二:使用std::forward实现完美转发

使用万能引用和完美转发来修改前面的例子,如下:

#include <iostream>template <typename T>
void wrapper(T &&u) { // 万能引用func(std::forward<T>(u)); // 完美转发
}class MyClass {};void func(MyClass& a) { std::cout << "in func(MyClass&)\n"; }
void func(const MyClass& a) { std::cout << "in func(const MyClass&)\n"; }
void func(MyClass&& a) { std::cout << "in func(MyClass &&)\n"; }int main(void) {MyClass a;const MyClass b;func(a);func(b);func(MyClass());std::cout << "----- Wrapper ------\n";wrapper(a);wrapper(b);wrapper(MyClass());return 0;
}

输出结果:

in func(MyClass&)
in func(const MyClass&)
in func(MyClass &&)
----- Wrapper ------
in func(MyClass&)
in func(const MyClass&)
in func(MyClass &&)Process returned 0 (0x0)   execution time : 0.210 s
Press any key to continue.

输出结果符合预期,使用std::forward实现了完美转发。

注意std::forward()建议仅用于模板函数,对于非模板的,因为不涉及到类型推导,所以使用完美转发是没有意义的。

https://mp.weixin.qq.com/s/bT–OIA7X8Uohxl_q1O98A

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

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

相关文章

Redis在生产环境中可能遇到的问题与解决方案(六)

26. Redis与微服务架构集成问题 问题描述 在微服务架构中&#xff0c;将 Redis 作为微服务之间的共享缓存时&#xff0c;可能遇到服务发现、动态配置等问题。 解决方案 服务注册与发现&#xff1a; 使用服务注册与发现工具确保微服务能够发现 Redis 实例。 动态配置&#x…

977.有序数组的平方(力扣LeetCode)

文章目录 977.有序数组的平方题目描述快速排序归并排序 977.有序数组的平方 题目描述 给你一个按 非递减顺序 排序的整数数组 nums&#xff0c;返回 每个数字的平方 组成的新数组&#xff0c;要求也按 非递减顺序 排序。 示例 1&#xff1a; 输入&#xff1a;nums [-4,-1,0…

vue超链接传值、查看页面以及父子传值

<el-table-column label"电表编码" align"center" width"120" prop"electricalNum" ><template slot-scope"scope"><div style"text-align: left"><router-link :to"/equipment/electr…

深度学习(6)---Transformer

文章目录 一、介绍二、架构2.1 Multi-head Attention2.2 Encoder(编码器)2.3 Decoder(解码器) 三、Encoder和Decoder之间的传递四、Training五、其他介绍5.1 Copy Mechanism5.2 Beam Search 一、介绍 1. Transformer是一个Seq2Seq&#xff08;Sequence-to-Sequence&#xff09;…

mapstruct自定义转换,怎样将String转化为List

源码&#xff1a;https://gitee.com/cao_wen_bin/test 最近在公司遇到了这样一个为题&#xff0c;前端传过来的是一个List<Manager>,往数据库中保存到时候是String&#xff0c;这个String使用谷歌的json转化器。 当查询的时候在将这个数据库中String的数据以List<Mana…

【知识---如何创建 GitHub 个人访问令牌】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言登录到 GitHub 帐户。在右上角的头像旁边&#xff0c;点击用户名&#xff0c;然后选择 "Settings"。在左侧导航栏中&#xff0c;选择 "Develope…

Mysql索引相关学习笔记:B+ Tree、索引分类、索引优化、索引失效场景及其他常见面试题

前言 索引是Mysql中常用到的一个功能&#xff0c;可以大大加快查询速度&#xff0c;同时面试中也是经常碰到。本文是学习Mysql索引的归纳总结。 索引采用的数据结构——B 树 本部分主要是参考自小林Coding B树的由来 二分查找可以每次缩减一半&#xff0c;从而提高查找效率…

对话框与多窗体设计 —— 标准对话框

三、对话框与多窗体设计3.1 标准对话框3.1.1 QFileDialog对话框3.1.2 QColorDialog对话框3.1.3 QFontDialog对话框3.1.4 QInputDialog标准输入对话框3.1.5 QMessageBox消息对话框 三、对话框与多窗体设计 一个完整的应用程序设计中&#xff0c;不可避免地会涉及多个窗 体、对框…

vue---打印本地当前时间Demo

<template><view class"content" tap"getCurrentTime()">打印时间</view> </template><script>export default {data() {return {title: Hello}},onLoad() {},methods: {getCurrentTime() {//获取当前时间并打印var _this …

论文写作之十个问题

前言 最近进入瓶颈&#xff1f; 改论文&#xff0c;改到有些抑郁了 总是不对&#xff0c;总是被打回 好的写作&#xff0c;让人一看就清楚明白非常重要 郁闷时候看看大佬们怎么说的 沈向洋、华刚&#xff1a;读科研论文的三个层次、四个阶段与十个问题 十问 What is the pro…

springboot127基于Springboot技术的实验室管理系统

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…

vue 跨域XMLHttpRequest

vue 跨域 使用XMLHttpRequest 亲测好使 let urlhttp://127.0.0.1:9000/pssnotifyyb?b1//urlhttps://api.j4u.ink/v1/store/other/proxy/remote/moyu.jsonvar xhrnew XMLHttpRequest()xhr.open(GET,url,true)//第三个参数是是否异步请求,默认true xhr.onreadystatec…

Elasticsearch内核解析 - 数据模型篇

Elasticsearch内核解析 - 数据模型篇 - 知乎 Elasticsearch是一个实时的分布式搜索和分析引擎&#xff0c;它可以帮助我们用很快的速度去处理大规模数据&#xff0c;可以用于全文检索、结构化检索、推荐、分析以及统计聚合等多种场景。 Elasticsearch是一个建立在全文搜索引擎…

SpringBoot增加全局traceId日志追踪

文章目录 实现思路定义过滤器返回参数增加traceIdlogback.xml增加traceId 实现思路 增加Filter处理请求&#xff0c;生成traceId保存到TreadLocal中&#xff08;slf4j的MDC&#xff09;增加返回AOP切面&#xff0c;返回数据之前把traceId写到返回实体里日志logback.xml文件配置…

蓝牙 | 软件: Qualcomm BT Audio 问题分析(1)----ACAT Tools安装

大家好&#xff01; 我是“声波电波还看今朝”成员的一位FAE Devin.wen&#xff0c;欢迎大家关注我们的账号。 今天给大家大概讲解“如何排查Qualcomm BT Audio”的疑难杂症&#xff08;一&#xff09;如何安装ACAT Tools。 大家在遇到Audio方面的问题&#xff0c;比如 无声、…

[蓝桥杯]真题讲解:飞机降落(DFS枚举)

[蓝桥杯]真题讲解&#xff1a;飞机降落&#xff08;DFS枚举&#xff09; 一、视频讲解二、暴力代码&#xff08;也是正解代码&#xff09; 一、视频讲解 视频讲解 二、暴力代码&#xff08;也是正解代码&#xff09; //飞机降落&#xff1a; 暴力枚举DFS #include<bits/…

hadoop 问题集

1. org.apache.hadoop.yarn.exceptions.InvalidAuxServiceException: The auxService:mapreduce_shuffle does not exist yarn中没有aux的信息。在yarn&#xff0d;site.xml中加入&#xff1a; <property> <name>yarn.nodemanager.aux-services</name> …

【python】自动微分的一个例子

一、例子 import torchx torch.arange(4.0) x.requires_grad_(True) y 2 * torch.dot(x, x) print(y) y.backward() x.grad 4 * x print(x.grad) 二、解读 1. import torch 这一行导入了PyTorch库。PyTorch是一个开源的机器学习库&#xff0c;广泛用于计算机视觉和自然语…

DAY10_SpringBoot—SpringMVC重定向和转发RestFul风格JSON格式SSM框架整合

目录 1 SpringMVC1.1 重定向和转发1.1.1 转发1.1.2 重定向1.1.3 转发练习1.1.4 重定向练习1.1.5 重定向/转发特点1.1.6 重定向/转发意义 1.2 RestFul风格1.2.1 RestFul入门案例1.2.2 简化业务调用 1.3 JSON1.3.1 JSON介绍1.3.2 JSON格式1.3.2.1 Object格式1.3.2.2 Array格式1.3…