当前位置: 首页 > news >正文

模板中的右值引用(万能引用)、引用折叠与完美转发

模板中的右值引用(万能引用)、引用折叠与完美转发

文章目录

  • 模板中的右值引用(万能引用)、引用折叠与完美转发
    • 一、万能引用与引用折叠
      • 1. 模板中的右值引用
      • 2. 自动类型推导(auto)与万能引用
      • 3. 引用折叠与万能引用
      • 4. lambda表达式捕获
      • 5. 条件转发
      • 6. 类型萃取
    • 二、完美转发
    • 总结

一、万能引用与引用折叠

1. 模板中的右值引用

​ 我们经常听万能引用,什么是万能引用,与普通的右值引用有什么区别?

下面给出一个案例,利用模板函数参数中的右值引用来展现万能引用的用途:

首先给出一个自定义的类型,简易的实现出拷贝构造函数和移动构造函数同时不必关注是否正常实现了功能,增加打印信息以便我们可以通过控制台明晰该函数是否被调用,何时被调用:

class Date
{
public:Date(int year, int month, int day):_year(year), _month(month), _day(day){cout << "Date(int year, int month, int day)" << endl;}Date(const Date& d){cout << "Date(const Date& d)" << endl;}Date(Date&& d){cout << "Date(Date&& d)" << endl;}
private:int _year;int _month;int _day;
};

接着给出两个测试函数,函数参数类型分别常量左值引用和右值引用:

// 常量左值引用函数模板
template<typename T>
void func1(const T& val)
{T d(val);
}
// 万能引用参数函数模板
template<typename T>
void func2(T&& val)
{T d(std::forward<T>(val));cout << "val是右值引用:" << std::is_rvalue_reference<decltype(val)>::value << endl;
}

注意上面 func2() 函数中用到的 forward() 相当于 move() 的进阶版,可以保证在函数间参数传递时保持原有左/右值类型。

继续给出测试用例:

Date d(2000, 1, 2);cout << "*********左值参数********" << endl;
func1(d);
cout << "*************************" << endl;
func2(d);cout << "*********右值参数********" << endl;
func1(std::move(d));
cout << "*************************" << endl;
func2(std::move(d));

运行结果:

在这里插入图片描述

通过上图我们认识到万能引用在模板中的重要性,万能引用是模板中大多数情况保证移动构造函数被正常调用的重要条件。只有参数是万能引用时,函数内部才可调用 std::forward<T>() 完美转发变量的左右值类型,并将保持源类型的变量转发给其他函数。

至于我们为什么称其为“万能引用”,虽然常量左值引用 “const T& val” 也能兼容接收左值和右值参数,但它不支持移动语义,因为它不允许修改绑定的对象因此,万能引用更有优势,因为它既支持移动语义,又能与完美转发结合使用,成功传递变量及其类型。

2. 自动类型推导(auto)与万能引用

当使用auto关键字时,万能引用可以帮助推导变量的类型。例如,auto&&可以根据初始化表达式是左值还是右值来推导出正确的类型。

void test2()
{Date d(2000, 1, 2);auto&& d1(d);cout << "d1是左值引用:" << std::is_lvalue_reference<decltype(d1)>::value << endl;cout << "d1是右值引用:" << std::is_rvalue_reference<decltype(d1)>::value << endl;auto&& d2(std::move(d));cout << "d2是左值引用:" << std::is_lvalue_reference<decltype(d2)>::value << endl;cout << "d2是右值引用:" << std::is_rvalue_reference<decltype(d2)>::value << endl;
}

在这里插入图片描述

通过上图我们发现 auto&& 可以在推导表达式类型时,同时推导其左值性或右值性。

3. 引用折叠与万能引用

引用折叠的规则解决了引用的引用(C++中不允许)的问题。这里是引用折叠的基本规则:

  • 如果两个引用中至少一个是左值引用(&),那么结果是左值引用(&)。
  • 如果两个引用都是右值引用(&&),那么结果是右值引用(&&)。

首先给出一个简单的以万能引用为参数的模版函数:

template<typename T>
void func(T&& val) {// val 是一个万能引用auto p = &val;cout << "左值引用:" << std::is_lvalue_reference<decltype(val)>::value << endl;cout << "右值引用:" << std::is_rvalue_reference<decltype(val)>::value << endl;
}

测试函数:

void test3()
{int x = 10;int& lx = x; // lx 是 x 的左值引用int&& rx = 10; // rx 是一个右值引用func(x);  // T 推导为 int&,因此 val 的类型为 int& &,折叠为 int&cout << "x是右值引用:" << std::is_rvalue_reference<decltype(x)>::value << endl;cout << "-------------------" << endl;func(lx); // T 推导为 int&,因此 val 的类型为 int& &,折叠为 int&cout << "lx是右值引用:" << std::is_rvalue_reference<decltype(lx)>::value << endl;cout << "-------------------" << endl;func(rx); // T 推导为 int&&,因此 val 的类型为 int&& &&,折叠为 int&&cout << "xr是右值引用:" << std::is_rvalue_reference<decltype(rx)>::value << endl;cout << endl;func(move(x));cout << "move(x)是右值引用:" << std::is_rvalue_reference<decltype(move(x))>::value << endl;cout << "-------------------" << endl;func(10); // T 推导为 int,因此 val 的类型为 int&&
}

运行结果:

在这里插入图片描述

对上面运行结果部分地方需要着重做出解释:

在这里插入图片描述

4. lambda表达式捕获

void test4()
{auto x = 5;auto lambda = [y = std::move(x)]() mutable {y += 2;return y;};cout << x << endl;cout << lambda() << endl;
}

当调用 lambda() 时,将输出 7,因为 y 被初始化为 5 并且增加了 2。请注意,由于 x 被移动到 yx 的值在移动后未定义,但通常在实际编译器实现中,基本类型的值在移动后保持不变。因此,输出 x 的值仍然是 5。

5. 条件转发

template<typename T>
void forwarder(T&& arg) {if constexpr (std::is_lvalue_reference_v<T>) {//process(arg); // 处理左值}else {//process(std::move(arg)); // 处理右值}
}

在这个例子中,forwarder 函数使用万能引用 T&& 来接受任何类型的参数,并根据参数的类型来决定调用哪个 process 函数。

6. 类型萃取

类型萃取(Type Traits)是模板元编程中的一种技术,它允许你在编译时检查类型信息或者修改类型。万能引用可以与类型萃取结合使用,以确定传递给模板的参数类型的属性。

template<typename T>
void process(T&& arg) {using Type = typename std::remove_reference<T>::type;if constexpr (std::is_integral_v<Type>) {// 如果 T 是整数类型//handle_integral(std::forward<T>(arg));}else {// 如果 T 不是整数类型//handle_non_integral(std::forward<T>(arg));}
}

在这个例子中,process 函数使用类型萃取来移除引用,并检查 T 是否为整数类型。然后它使用 std::forward 来保持参数的值类别,并将其传递给相应的处理函数。

二、完美转发

在这里插入图片描述

​ 实际上这里才真正提起完美转发这个概念,看了本文前面内容也已经大概了解,我们这里仅仅将其抽离出来进行特别总结。

  • 首先,我们已经知道右值引用变量具有左值属性,因为其需要保留可修改性,所以自然不能是右值。

  • 其次,传递给右值引用类型的参数在传参后会退化为左值

所以我们想要在万能引用作参数的函数内部实现分离操作,需要对参数的左右值属性进行判断,例如:

template<typename T>
void print_right(T&& v)
{cout << "right -> " << v << endl;
}
template<typename T>
void print_left(const T& v)
{cout << "left -> " << v << endl;
}template<typename T>
void do_something(T&& val)
{if (std::is_rvalue_reference<decltype(val)>::value)	// val是右值{print_right(std::forward<T>(val));	// 利用完美转发将退化逆向为初始类型,此处判断初始右值,也可 move(val)}else	// val是左值{print_left(val);		// val退化为左值,直接传递}
}

测试函数:

void test5()
{int x = 18;//do_something<int>(x);		// 编译报错do_something<int&>(x);	// 注意这里x是左值,所以模板参数为int编译会报错,模板参数和函数参数类型需要统一满足引用折叠do_something<int>(10);
}

注意上面测试代码的案例也是引用折叠的重要体现

对于完美转发 forward(val) 中 val 的属性可能是左值也可能是右值:

  • 当 val 是左值时,forward 对左值 val 不做处理
  • 当 val 是右值时,forward 的作用相当于 move(val)

在这里插入图片描述


总结

​ 本文被三个词语所贯穿:万能引用、引用折叠、完美转发。这三个概念之间的联系紧密以及使用场景高度重合,正是因为C++11中提出如此富有意义的新概念,极大地方便了我们重构代码,理想高效地编码实现功能。

相关文章:

模板中的右值引用(万能引用)、引用折叠与完美转发

模板中的右值引用&#xff08;万能引用&#xff09;、引用折叠与完美转发 文章目录 模板中的右值引用&#xff08;万能引用&#xff09;、引用折叠与完美转发一、万能引用与引用折叠1. 模板中的右值引用2. 自动类型推导(auto)与万能引用3. 引用折叠与万能引用4. lambda表达式捕…...

Nacos启动报错:[db-load-error]load jdbc.properties error

在学习Nacos中间件时&#xff0c;出现了一个错误&#xff0c;竟然启动报错&#xff01;&#xff01;&#xff01;! 这个错误第一次遇见&#xff0c;当时我感觉大体就是--数据库连接方面的错误。 可是&#xff0c;对于初学者的我来说一脸懵啊&#xff1f;&#xff1f;&#xff…...

5.23相关性分析

相关性分析是一件很自然而然的事情&#xff0c;在生活中和科学研究中&#xff0c;我们都可能会不由自主地关注两件或者多件事情之间的联系。比如性别和方向感有没有关系&#xff0c;有多大关系&#xff0c;辨别不同事物时如何说明特征的科学性&#xff08;也就是该特征和事物的…...

使用 Sonatype Nexus Repository Manager 如何安装npm.md

1. 安装与启动 Nexus2. 登录 Nexus Web UI3. 创建 npm 仓库4. &#xff08;可选&#xff09;配置 npm 代理仓库5. 创建 npm 仓库组6. 配置 npm 客户端7. 测试和使用 Sonatype Nexus Repository Manager (通常简称 Nexus) 是一个强大的二进制管理系统&#xff0c;用于存储和管理…...

console如何连接远程机器上的java程序

启动参数 -Djava.rmi.server.hostname192.168.1.10 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port12345 -Dcom.sun.management.jmxremote.sslfalse -Dcom.sun.management.jmxremote.authenticatefalse2.jdk安装目录/bin下执行 go jconsole![在这里插入…...

高稳定数显芯片防干扰抗噪数码屏驱动高亮LED驱动IC-VK16K33A/AA 最大13×3的按键扫描

产品型号&#xff1a;VK16K33A/AA 产品品牌&#xff1a;永嘉微电/VINKA 封装形式&#xff1a;SOP28/SSOP28 原厂&#xff0c;工程服务&#xff0c;技术支持&#xff01; 概述 VK16K33A/AA是一种带按键扫描接口的数码管或点阵LED驱动控制专用芯片&#xff0c;内部集成有数据…...

Redis离线安装(单机)

目录 1-环境准备1-1下载redis-4.0.11.tar.gz1-2gcc环境 2-上传解压3-编译安装(需要gcc环境)4-配置redis5-启动Redis6-开启防火墙(root)7-添加开机启动脚本8-设置权限9-设置开机启动10-测试redis服务11-检查是否安装成功12-创建redis命令软连接13-测试redis14-必要时设置防火墙 …...

[Algorithm][动态规划][路径问题][不同路径][不同路径Ⅱ][珠宝的最高价值]详细讲解

目录 1.不同路径1.题目链接2.算法原理详解3.代码实现 2.不同路径 II1.题目链接2.算法原理详解3.代码实现 3.珠宝的最高价值1.题目链接2.算法原理详解3.代码实现 1.不同路径 1.题目链接 不同路径 2.算法原理详解 思路&#xff1a; 确定状态表示 -> dp[i][j]的含义 走到dp[…...

ChatGPT移动应用收入在GPT-4o发布后迎来最大涨幅

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…...

汉语拼音 如何 转化成粤语拼音 的

将汉语拼音&#xff08;普通话拼音&#xff09;转化为粤语拼音涉及到对声母、韵母以及声调的对照和调整。以下是详细的转换步骤和注意事项&#xff1a; 一、转换步骤 识别普通话拼音的声母和韵母查找对应的粤语拼音声母和韵母应用粤语声调 二、声母对照表 普通话拼音粤语拼…...

本地电子邮件测试工具-MailHog

通过MailHog&#xff0c;可以在浏览器中查看本机发的邮件内容&#xff0c;而无需发送到外网。 https://github.com/mailhog/MailHog在 macOS 环境下&#xff0c;下载文件后: 添加可执行权限:chmod x MailHog_darwin_amd64 运行:./MailHog_darwin_amd64 浏览器打开查看邮件:htt…...

Java18新特性

Java 18引入了若干新特性&#xff0c;以增强语言的功能性和性能。具体如下&#xff1a; 服务提供者接口&#xff08;Service Provider Interfaces, SPI&#xff09;&#xff1a;允许开发者为Java模块系统定义服务加载机制&#xff0c;从而能够更灵活地发现和加载服务实现。简单…...

大象资讯:PostgreSQL 17 Beta 1 发布!

↑ 关注“少安事务所”公众号&#xff0c;欢迎⭐收藏&#xff0c;不错过精彩内容~ PostgreSQL 全球开发小组 发布于 2024-05-23 PostgreSQL 全球开发小组宣布&#xff0c;PostgreSQL 17 的第一个测试版本现已可供下载。此版本包含 PostgreSQL 17 正式发布时将提供的所有功能的预…...

Rust:如何在 Windows 的 Linux 子系统(WSL)下安装

一、安装步骤 在Windows Subsystem for Linux (WSL)下安装Rust&#xff0c;可以按照以下步骤进行&#xff1a; 打开WSL终端&#xff1a; 首先&#xff0c;确保你的WSL已经安装并正常运行。你可以在Windows搜索栏中输入“WSL”并选择你安装的Linux发行版&#xff08;如Ubuntu&a…...

工具分享:VsCode注释神器,koro1FileHeader

他是有官方Wiki的。 https://github.com/OBKoro1/koro1FileHeader/wiki/ 项目在GitHub上开源。以下摘录部分wiki&#xff0c;用作介绍分享在这里插入代码片 如何找到setting.json设置模板 简单的输入命令 打开VSCode命令面板: mac: command p window: ctrl p输入> Ope…...

水面漂浮物生活垃圾识别检测系统

水面漂浮物生活垃圾识别检测系统通过现场监控摄像机对河道湖面等水体进行实时监测&#xff0c;水面漂浮物生活垃圾识别检测系统借助智能视频分析技术和YOLO深度学习技术&#xff0c;系统能够自动识别和抓拍水面上的垃圾漂浮物。一旦系统检测到有垃圾漂浮在水面上&#xff0c;立…...

通过python读取并发送二进制文件到串口

代码 #!python.exe """ filename send_bin.py brief According to the users input, read bin file, subpackage and send the file by UART. HowToUse send_bin.py -h author shadowThreeDgmail.com data 20220224 &q…...

前端笔记-day07

学成在线网站 文章目录 效果图代码展示index.htmlindex.cssbase.css 效果图 代码展示 index.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-w…...

【MySQL精通之路】INFORMATION_SCHEMA库-INNODB_METRICS表

INNODB_METRICS表提供了各种各样的INNODB性能信息&#xff0c;补充了INNODB性能模式表的特定重点领域。通过简单的查询&#xff0c;您可以检查系统的整体运行状况。通过更详细的查询&#xff0c;您可以诊断诸如性能瓶颈、资源短缺和应用程序问题等问题。 每个监视器表示InnoDB…...

React Native 之 定义全局状态管理库(九)

假设你正在使用基于单页面应用&#xff08;SPA&#xff09;的微前端框架。以下简化一个应用之间共享状态的例子。 1. 使用发布/订阅模式 // globalStateManager.js class GlobalStateManager { constructor() { this.subscribers {}; this.state {}; } subscribe(key…...

AI证明数学猜想、Spotify用AI翻唱付费、OpenTelemetry毕业:今天科技圈发生了什么

每天更新&#xff0c;带你读懂科技圈。 今日看点&#xff1a; OpenAI模型推翻了困扰数学界80年的离散几何猜想&#xff0c;AI在纯数学领域迈出关键一步&#xff1b;Spotify与环球音乐达成AI翻唱分成协议&#xff0c;音乐行业正式拥抱生成式AI&#xff1b;云原生可观测性标准Ope…...

Steam创意工坊下载器深度解析:WorkshopDL架构揭秘与实战指南

Steam创意工坊下载器深度解析&#xff1a;WorkshopDL架构揭秘与实战指南 【免费下载链接】WorkshopDL WorkshopDL - The Best Steam Workshop Downloader 项目地址: https://gitcode.com/gh_mirrors/wo/WorkshopDL 在跨平台游戏生态日益成熟的今天&#xff0c;Steam创意…...

如何在OneNote 2016中实现专业级代码高亮?NoteHighlight2016完整使用指南

如何在OneNote 2016中实现专业级代码高亮&#xff1f;NoteHighlight2016完整使用指南 【免费下载链接】NoteHighlight2016 Source code syntax highlighting for OneNote 2016 and OneNote for O365 . NoteHighlight 2013 port for OneNote 2016 (32-bit and 64-bit) 项目地址…...

抖音内容批量下载终极指南:免费保存视频、图集、音乐和直播回放

抖音内容批量下载终极指南&#xff1a;免费保存视频、图集、音乐和直播回放 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallb…...

如何在Python中创建测试图像

原文地址&#xff1a;https://medium.com/itberrios6/how-to-make-a-test-image-in-python-1a6c2d41b6ab 学习如何制作测试图像 在计算机视觉和图像处理中&#xff0c;创建测试图像以更好地了解算法或滤波器将如何执行通常是有用的。测试图像是一个基准&#xff0c;可以将多种…...

ContentBranch+CFBranch混合电影推荐模型|全网独家复现,深度学习实战篇 引入双分支融合架构,兼顾内容特征与协同信号、助力冷启动缓解、数据稀疏性优化、推荐精度有效涨点

目录 一、前言:混合推荐模型的核心价值与行业痛点 二、模型核心原理(全网独家拆解,通俗易懂) 2.1 整体架构逻辑 2.2 ContentBranch(内容分支)原理详解 2.3 CFBranch(协同过滤分支)原理详解 2.4 特征融合与预测层原理 2.5 模型优势总结 三、环境搭建(全平台适配…...

路由算法的终极真相:为何“绝对最佳”是伪命题?从理论陷阱到工程实战的深度破局

路由算法的终极真相&#xff1a;为何“绝对最佳”是伪命题&#xff1f;从理论陷阱到工程实战的深度破局 摘要&#xff1a;在计算机网络的浩瀚星图中&#xff0c;路由选择算法如同指引数据包穿越迷雾的灯塔。然而&#xff0c;无数工程师和架构师曾陷入一个巨大的思维误区&#x…...

基于Rust与Skia构建高性能跨平台文本编辑器的架构设计与实现

1. 项目概述&#xff1a;为什么我们需要一款“超越者”&#xff1f;在程序员和文本工作者的日常工具箱里&#xff0c;文本编辑器占据着举足轻重的地位。它不像IDE那样庞大臃肿&#xff0c;却需要具备处理代码、日志、配置文件的强大能力。长久以来&#xff0c;Notepad以其轻量、…...

嵌入式开发通用工具包设计:模块化、可裁剪与高性能实现

1. 项目概述&#xff1a;为什么嵌入式开发需要一个“瑞士军刀”&#xff1f;在嵌入式开发的日常里&#xff0c;我猜你和我一样&#xff0c;经常在重复造轮子。比如&#xff0c;今天在A项目里写了个精巧的CRC校验函数&#xff0c;明天在B项目里又要处理环形缓冲区&#xff0c;后…...

京东自动抢购工具:5分钟快速上手指南,轻松抢购心仪商品

京东自动抢购工具&#xff1a;5分钟快速上手指南&#xff0c;轻松抢购心仪商品 【免费下载链接】autobuy-jd 使用python语言的京东平台抢购脚本 项目地址: https://gitcode.com/gh_mirrors/au/autobuy-jd 还在为心仪商品秒杀时手速不够快而烦恼吗&#xff1f;Autobuy-JD…...