C++模板元编程(二)——完美转发

完美转发指的是函数模板可以将自己的参数“完美”地转发给内部调用的其它函数。所谓完美,即不仅能准确地转发参数的值,还能保证被转发参数的左、右值属性不变。


文章目录

  • 场景
  • 旧的方法
  • 新的方法
  • 内部实现
  • 参考文献


场景

思考下面的代码:

template<typename T>
void function(T t) {
    otherfunc(t);
}

function() 函数模板中调用了otherfunc()函数。我们想要的完美转发是:

  • 如果function()函数接收到的参数 t 为左值,那么该函数传递给 otherfunc() 的参数 t 也是左值。
  • 如果function()函数接收到的参数t为右值,那么传递给 otherfunc() 函数的参数 t 也是右值

显然 function() 函数模板没有实现完美转发,这是因为无论是左值还是右值传递进来,都会当作是左值,因为是非引用类型。

比如 function(10); 传递给 otherfunc() 也是左值而不是我们期望的右值,这在我们期望对左值和右值进行不同处理时会产生问题。


旧的方法

在C++98/03 标准下的 C++ 也可以实现完美转发,只是实现方式比较麻烦。

C++98/03 标准中只有左值引用,可以细分为非 const引用和const引用:

  • 非const引用作为函数模板参数,只能接收左值无法接收右值
  • const左值引用既可以接收左值,也可以接收右值,但如果内部需要将参数传递给其他函数,需要被调用函数的参数也是 const,否则无法直接传递

可见能实现转发,但不够"完美"。

#include <iostream>
using namespace std;
//重载被调用函数,查看完美转发的效果
void otherfunc(int & t) {
    cout << "call lvalue\n";
}
void otherfunc(const int & t) {
    cout << "call rvalue\n";
}

//重载函数模板,分别接收左值和右值
//接收右值参数
template <typename T>
void function(const T& t) {
    otherfunc(t);
}
//接收左值参数
template <typename T>
void function(T& t) {
    otherfunc(t);
}
int main()
{
    function(10);//10 是右值
    int  x = 2;
    function(x);//x 是左值
    return 0;
}

输出为

左值实参既能匹配 function(T& t) 也能匹配 function(const& t),编译器会选择更合适的 function(T& t)


新的方法

对于旧的方法,当模板函数有大量参数的情况,可能就需要编写大量的重载函数模板。

在C++11标准中引入了右值引用,通常情况下右值引用只能接收右值,而对于函数模板中使用右值引用语法定义的参数来说,它既可以接收右值,也可以接收左值(称为万能引用)。

因此在C++11标准中实现完美转发,只需要编写如下一个模板函数即可:

template <typename T>
void function(T&& t) {
	otherdef(t);
}

但是还存在一个问题,如果我们传入的参数是一个左值引用或右值引用的实参,如下所示:

int x = 5;
int& y =  x;
function(y); // T为int&
int&& z = 1;
function(z); // T 为int&&

其中, function(y) 实例化的函数为 function(int& && t),由function(z) 实例化的函数为 function(int&& &&t),这在C++98/03是不支持的,而C++11引入了引用折叠规则:

  • 当实参为左值或者左值引用(A&)时,函数模板中 T&& 将转变为 A&,即 A& && = A&
  • 当实参为右值或者右值引用(A&&)时,函数模板中 T&& 将转变为 A&&,即 A&& && = A&&

还存在的问题是,在function()函数内部,不管是 T& t 还是 T&& t 其实 t 都是一个左值,因此都会传递到 otherfunc(int& t)

所以我们需要一种解决方案来处理这个问题,C++11标准里的模板函数 forward() 就是用来解决这个问题,让我们能传递左值/右值属性,例子如下:

#include <iostream>
using namespace std;
//重载被调用函数,查看完美转发的效果
void otherfunc(int & t) {
    cout << "lvalue\n";
}
void otherfunc(const int & t) {
    cout << "rvalue\n";
}
//实现完美转发的函数模板
template <typename T>
void function(T&& t) {
    otherfunc(forward<T>(t));
}
int main()
{
    function(1);
    int  x = 2;
    function(x);
    return 0;
}

输出如下,正确传递了左值/右值属性


内部实现

下面简单看下内部实现(MSVC)

_EXPORT_STD template <class _Ty>
_NODISCARD _MSVC_INTRINSIC constexpr _Ty&& forward(remove_reference_t<_Ty>& _Arg) noexcept {
    return static_cast<_Ty&&>(_Arg);
}

_EXPORT_STD template <class _Ty>
_NODISCARD _MSVC_INTRINSIC constexpr _Ty&& forward(remove_reference_t<_Ty>&& _Arg) noexcept {
    static_assert(!is_lvalue_reference_v<_Ty>, "bad forward call");
    return static_cast<_Ty&&>(_Arg);
}

简单地来说就是通过静态的强制类型转换+引用折叠,返回对应的结果。

比如下面的四钟情况:

	int x = 2;
	otherfunc(forward<int>(x)); // 匹配第一个,返回 int&&
	otherfunc(forward<int>(2)); // 匹配第二个,返回 int&&
	int& y = x;
	otherfunc(forward<int&>(y)); // 匹配第一个,返回 int&
	int&& z = 2;
	otherfunc(forward<int&&>(z)); // 匹配第一个,返回 int&&,可见右值引用是个左值

它的输出结果如下


参考文献

C++11、C++14、C++17、C++20新特性总结(5万字详解)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深度学习之网络构建

目标 选择合适的神经网络 卷积神经网络&#xff08;CNN&#xff09;&#xff1a;我们处理图片、视频一般选择CNN 循环神经网络&#xff08;RNN&#xff09;&#xff1a;我们处理时序数据一般选择RNN 超参数的设置 为什么训练的模型的错误率居高不下 如何调测出最优的超参数 …

【pytorch20】多分类问题

网络结构以及示例 该网络的输出不是一层或两层的&#xff0c;而是一个十层的代表有十分类 新建三个线性层&#xff0c;每个线性层都有w和b的tensor 首先输入维度是784&#xff0c;第一个维度是ch_out,第二个维度才是ch_in(由于后面要转置)&#xff0c;没有经过softmax函数和…

C++ 引用——引用的本质

本质&#xff1a;引用的本质在c内部实现是一个指针常量 C推荐用引用技术&#xff0c;因为语法方便&#xff0c;引用本质是指针常量&#xff0c;但是所有的指针操作编译器都帮我们做了 示例&#xff1a; 运行结果&#xff1a;

C++初学者指南-4.诊断---valgrind

C初学者指南-4.诊断—Valgrind Valgrind&#xff08;内存错误检测工具&#xff09; 检测常见运行时错误 读/写释放的内存或不正确的堆栈区域使用未初始化的值不正确的内存释放&#xff0c;如双重释放滥用内存分配函数内存泄漏–非故意的内存消耗通常与程序逻辑缺陷有关&#xf…

水箱高低水位浮球液位开关工作原理

工作原理 水箱高低水位浮球液位开关是一种利用浮球随液位升降来实现液位控制的设备。其基本原理是浮球在液体的浮力作用下上下浮动&#xff0c;通过磁性作用驱动与之相连的磁簧开关的开合&#xff0c;从而实现液位的高低控制和报警。当液位升高时&#xff0c;浮球上浮&#xf…

cmake find_package 使用笔记

目录 1 find_package2 config mode2.1 搜索的文件名2.2 搜索路径 3 module mode3.1 搜索的文件名3.2 搜索路径 参考 1 find_package 这是官方文档 下面是学习总结&#xff1a; 首先是find_package的作用是什么&#xff1f;引入预编译的库。 find_package有两种模式&#xff1a…

C语言 指针和数组——指针和二维数组之间的关系

目录 换个角度看二维数组 指向二维数组的行指针 按行指针访问二维数组元素 再换一个角度看二维数组 按列指针访问二维数组元素 二维数组作函数参数 指向二维数组的行指针作函数参数 指向二维数组的列指针作函数参数​编辑 用const保护你传给函数的数据 小结 换个角度看…

使用antd的<Form/>组件获取富文本编辑器输入的数据

前端开发中&#xff0c;嵌入富文本编辑器时&#xff0c;可以通过富文本编辑器自身的事件处理函数将数据传输给后端。有时候&#xff0c;场景稍微复杂点&#xff0c;比如一个输入页面除了要保存富文本编辑器的内容到后端&#xff0c;可能还有一些其他输入组件获取到的数据也一并…

Win10安装MongoDB(详细版)

文章目录 1、安装MongoDB Server1.1. 下载1.2. 安装 2、手动安装MongoDB Compass(GUI可视工具)2.1. 下载2.2.安装 3、测试连接3.1.MongoDB Compass 连接3.2.使用Navicat连接 1、安装MongoDB Server 1.1. 下载 官网下载地址 https://www.mongodb.com/try/download/community …

『大模型笔记』《Pytorch实用教程》(第二版)

『大模型笔记』《Pytorch实用教程》(第二版) 文章目录 一. 《Pytorch实用教程》(第二版)1.1 上篇1.2 中篇1.3 下篇1.4 本书亮点1.5 本书内容及结构二. 参考文献🖥️ 配套代码(开源免费):https://github.com/TingsongYu/PyTorch-Tutorial-2nd📚 在线阅读(开源免费)…

nginx相关概念(反向代理、负载均衡)

1 Nginx 是什么 Nginx是一款轻量级的Web 服务器&#xff0c;其特点是占有内存少&#xff0c;并发能力强 2 Nginx 反向代理 正向代理代替客户端去发送请求反向代理代替服务端接受请求 2.1 正向代理 若客户端无法直接访问到目标服务器 server 则客户端需要配置代理服务器 pr…

正则表达式 先行断言 \S {} 示例

目录 数据准备一. 先行断言1.1 正向先行断言1.2 负向先行断言 二. 配合 {} 和 \S 使用2.1 匹配一个任意非空白字符2.2 匹配任意多个非空白字符2.3 匹配3个非空白字符2.4 匹配至少3个非空白字符2.5 匹配0~3个非空白字符 数据准备 ⏹文件1 0561-10 AAA 123 dfg 345 sss 0561-2…

【电路笔记】-AB类放大器

AB类放大器 文章目录 AB类放大器1、概述2、AB类放大器介绍3、AB类放大器效率4、偏置方法4.1 电压偏置4.2 分压网络4.3 电位器偏置4.4 二极管偏置5、二极管网络和电流源6、AB类放大器的电源分配7、总结1、概述 A类放大器提供非常好的输出线性度,这意味着可以忠实地再现信号,但…

数据结构之“队列”(全方位认识)

&#x1f339;个人主页&#x1f339;&#xff1a;喜欢草莓熊的bear &#x1f339;专栏&#x1f339;&#xff1a;数据结构 前言 上期博客介绍了” 栈 “这个数据结构&#xff0c;他具有先进后出的特点。本期介绍“ 队列 ”这个数据结构&#xff0c;他具有先进先出的特点。 目录…

【无标题】ubuntu的安装和登录

1.安装ubuntu系统并允许root用户登录 一、安装ubuntu分为以下几个步骤&#xff1a; a、在官方网站下载镜像&#xff1a;https://ubuntu.com/ b、选择安装模式为&#xff1a;Try or install Ubuntu &#xff08;体验或安装&#xff09; c、选择体验系统或安装系统&#xff1a…

ComfyUI预处理器ControlNet简单介绍与使用(附件工作流)

简介 ControlNet 是一个很强的插件&#xff0c;提供了很多种图片的控制方式&#xff0c;有的可以控制画面的结构&#xff0c;有的可以控制人物的姿势&#xff0c;还有的可以控制图片的画风&#xff0c;这对于提高AI绘画的质量特别有用。接下来就演示几种热门常用的控制方式 1…

RAG理论:ES混合搜索BM25+kNN(cosine)以及归一化

接前一篇:RAG实践:ES混合搜索BM25+kNN(cosine) https://blog.csdn.net/Xin_101/article/details/140230948 本文主要讲解混合搜索相关理论以及计算推导过程, 包括BM25、kNN以及ES中使用混合搜索分数计算过程。 详细讲解: (1)ES中如何通过BM25计算关键词搜索分数; (2)…

【手写数据库内核组件】0201 哈希表hashtable的实战演练,多种非加密算法,hash桶的冲突处理,查找插入删除操作的代码实现

hash表原理与实战 ​专栏内容&#xff1a; postgresql使用入门基础手写数据库toadb并发编程 个人主页&#xff1a;我的主页 管理社区&#xff1a;开源数据库 座右铭&#xff1a;天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物. 文章目录 hash表…

下载linux的吐槽

本来这几天放假了&#xff0c;想下一个linux玩一玩 教程&#xff08;我就是根据这个教程进行下载的&#xff0c;但是呢在进行修改BIOS 模式的 地方遇见了困难&#xff0c;也许是电脑修过的原因&#xff0c;我狂按F12 以及 FnF12都没有BIOS设置&#xff0c;只有一个让我选择用w…

第二次练习

目录 一、student表的增删改查 1.向student表中添加一条新记录 2. 向student表中添加多条新记录 3.向student表中添加一条新记录 4.更新表&#xff0c;grade 大于90的加0.5 5.删除成绩为空的记录 二、用户权限部分 1、创建一个用户test1使他只能本地登录拥有查询student表的权…