模板中的右值引用(万能引用)、引用折叠与完美转发
模板中的右值引用(万能引用)、引用折叠与完美转发
文章目录
- 模板中的右值引用(万能引用)、引用折叠与完美转发
- 一、万能引用与引用折叠
- 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 被移动到 y,x 的值在移动后未定义,但通常在实际编译器实现中,基本类型的值在移动后保持不变。因此,输出 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中提出如此富有意义的新概念,极大地方便了我们重构代码,理想高效地编码实现功能。
相关文章:
模板中的右值引用(万能引用)、引用折叠与完美转发
模板中的右值引用(万能引用)、引用折叠与完美转发 文章目录 模板中的右值引用(万能引用)、引用折叠与完美转发一、万能引用与引用折叠1. 模板中的右值引用2. 自动类型推导(auto)与万能引用3. 引用折叠与万能引用4. lambda表达式捕…...
Nacos启动报错:[db-load-error]load jdbc.properties error
在学习Nacos中间件时,出现了一个错误,竟然启动报错!!!! 这个错误第一次遇见,当时我感觉大体就是--数据库连接方面的错误。 可是,对于初学者的我来说一脸懵啊??ÿ…...
5.23相关性分析
相关性分析是一件很自然而然的事情,在生活中和科学研究中,我们都可能会不由自主地关注两件或者多件事情之间的联系。比如性别和方向感有没有关系,有多大关系,辨别不同事物时如何说明特征的科学性(也就是该特征和事物的…...
使用 Sonatype Nexus Repository Manager 如何安装npm.md
1. 安装与启动 Nexus2. 登录 Nexus Web UI3. 创建 npm 仓库4. (可选)配置 npm 代理仓库5. 创建 npm 仓库组6. 配置 npm 客户端7. 测试和使用 Sonatype Nexus Repository Manager (通常简称 Nexus) 是一个强大的二进制管理系统,用于存储和管理…...
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的按键扫描
产品型号:VK16K33A/AA 产品品牌:永嘉微电/VINKA 封装形式:SOP28/SSOP28 原厂,工程服务,技术支持! 概述 VK16K33A/AA是一种带按键扫描接口的数码管或点阵LED驱动控制专用芯片,内部集成有数据…...
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.算法原理详解 思路: 确定状态表示 -> dp[i][j]的含义 走到dp[…...
ChatGPT移动应用收入在GPT-4o发布后迎来最大涨幅
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...
汉语拼音 如何 转化成粤语拼音 的
将汉语拼音(普通话拼音)转化为粤语拼音涉及到对声母、韵母以及声调的对照和调整。以下是详细的转换步骤和注意事项: 一、转换步骤 识别普通话拼音的声母和韵母查找对应的粤语拼音声母和韵母应用粤语声调 二、声母对照表 普通话拼音粤语拼…...
本地电子邮件测试工具-MailHog
通过MailHog,可以在浏览器中查看本机发的邮件内容,而无需发送到外网。 https://github.com/mailhog/MailHog在 macOS 环境下,下载文件后: 添加可执行权限:chmod x MailHog_darwin_amd64 运行:./MailHog_darwin_amd64 浏览器打开查看邮件:htt…...
Java18新特性
Java 18引入了若干新特性,以增强语言的功能性和性能。具体如下: 服务提供者接口(Service Provider Interfaces, SPI):允许开发者为Java模块系统定义服务加载机制,从而能够更灵活地发现和加载服务实现。简单…...
大象资讯:PostgreSQL 17 Beta 1 发布!
↑ 关注“少安事务所”公众号,欢迎⭐收藏,不错过精彩内容~ PostgreSQL 全球开发小组 发布于 2024-05-23 PostgreSQL 全球开发小组宣布,PostgreSQL 17 的第一个测试版本现已可供下载。此版本包含 PostgreSQL 17 正式发布时将提供的所有功能的预…...
Rust:如何在 Windows 的 Linux 子系统(WSL)下安装
一、安装步骤 在Windows Subsystem for Linux (WSL)下安装Rust,可以按照以下步骤进行: 打开WSL终端: 首先,确保你的WSL已经安装并正常运行。你可以在Windows搜索栏中输入“WSL”并选择你安装的Linux发行版(如Ubuntu&a…...
工具分享:VsCode注释神器,koro1FileHeader
他是有官方Wiki的。 https://github.com/OBKoro1/koro1FileHeader/wiki/ 项目在GitHub上开源。以下摘录部分wiki,用作介绍分享在这里插入代码片 如何找到setting.json设置模板 简单的输入命令 打开VSCode命令面板: mac: command p window: ctrl p输入> Ope…...
水面漂浮物生活垃圾识别检测系统
水面漂浮物生活垃圾识别检测系统通过现场监控摄像机对河道湖面等水体进行实时监测,水面漂浮物生活垃圾识别检测系统借助智能视频分析技术和YOLO深度学习技术,系统能够自动识别和抓拍水面上的垃圾漂浮物。一旦系统检测到有垃圾漂浮在水面上,立…...
通过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性能信息,补充了INNODB性能模式表的特定重点领域。通过简单的查询,您可以检查系统的整体运行状况。通过更详细的查询,您可以诊断诸如性能瓶颈、资源短缺和应用程序问题等问题。 每个监视器表示InnoDB…...
React Native 之 定义全局状态管理库(九)
假设你正在使用基于单页面应用(SPA)的微前端框架。以下简化一个应用之间共享状态的例子。 1. 使用发布/订阅模式 // globalStateManager.js class GlobalStateManager { constructor() { this.subscribers {}; this.state {}; } subscribe(key…...
【QT】Layout布局间隙优化全攻略(参数调整与实战技巧)
1. 为什么你的QT界面总有"迷之缝隙"? 每次用QT做界面开发时,最让我抓狂的就是那些莫名其妙出现的空白间隙。明明已经按照设计稿精确设置了控件尺寸,但运行起来总会出现几个像素的偏差。后来我发现,这些间隙主要来自三个…...
ARMv8、AArch64 与 arm64:命名与体系结构要点
ARMv8、AArch64 与 arm64:命名与体系结构要点 ARMv8 指 ARM 架构的一个主版本代际;AArch64 是该代际下的 64 位执行状态与 A64 指令集;arm64 与 aarch64 是操作系统与工具链中对 AArch64 的常用三元组/目录名,二进制约定一致。下…...
保姆级教程:在WSL上用AWS CLI配置MinIO临时访问凭证(含时区避坑指南)
在WSL中实战MinIO临时凭证:从配置到避坑的全流程指南 如果你正在Windows系统上使用WSL进行开发,并且需要为MinIO对象存储生成临时访问凭证,那么这篇文章将为你提供完整的解决方案。我们将从环境准备开始,逐步深入到凭证生成、策略…...
VRM-Addon-for-Blender:虚拟角色创作全流程指南
VRM-Addon-for-Blender:虚拟角色创作全流程指南 【免费下载链接】VRM-Addon-for-Blender VRM Importer, Exporter and Utilities for Blender 2.93 or later 项目地址: https://gitcode.com/gh_mirrors/vr/VRM-Addon-for-Blender VRM-Addon-for-Blender是一款…...
电力电子器件全解析:从二极管到IGBT,手把手教你掌握王兆安教材核心考点
电力电子器件深度解析:从基础原理到高效复习策略 电力电子技术作为现代自动化与能源转换的核心学科,其器件特性与应用的掌握程度直接影响着工程师解决实际问题的能力。对于华南理工大学自动化专业的学生而言,王兆安教授的《电力电子技术》教材…...
中文句子相似度分析神器:StructBERT本地部署全流程详解(附代码)
中文句子相似度分析神器:StructBERT本地部署全流程详解(附代码) 1. 工具概览与核心价值 中文语义相似度分析是自然语言处理中的一项基础但关键的任务。无论是智能客服中的问题匹配,还是内容平台的文章查重,都需要准确…...
从Safetensors到GGUF:利用llama.cpp解锁Ollama模型部署新路径
1. 为什么需要从Safetensors转换到GGUF格式 最近在魔塔社区下载了几个热门的大模型,发现都是safetensors格式的,但直接扔进Ollama里根本跑不起来。这个问题困扰了我好几天,直到发现了llama.cpp这个神器。safetensors其实是Hugging Face生态中…...
dfs:飞机降落
题目:P9241 [蓝桥杯 2023 省 B] 飞机降落 - 洛谷 做题目之前一定要先看数据范围。这道题的数据范围,T,N均<10,可以用暴力搜索。 这道题是排序,假设有3辆飞机。顺序可以是123,132,213,231&am…...
OpenClaw自动化周报:Qwen3-32B镜像整合多平台数据
OpenClaw自动化周报:Qwen3-32B镜像整合多平台数据 1. 为什么需要自动化周报 每周五下午,我的日历总会准时弹出提醒:"撰写本周工作总结"。这个看似简单的任务,实际操作起来却异常繁琐:需要登录JIRA查看任务…...
好看不等于会交互!阿里发布基于交互的世界模型基准
视频生成技术正在以惊人的速度迭代,那些光影绚丽的画面常常让人惊叹人工智能的创造力,但当你仔细观察视频中的物理碰撞或物体运动时,会发现它们常常并不符合现实世界的常识。由阿里、中科院、北航和北邮的研究人员联合推出的 Omni-WorldBench…...
