【模板进阶】类型参数的推断

news2024/9/21 2:40:45

一、使用 B o o s t Boost Boost库来借助推断

通常,我们可以使用 t y p e i d ( ) typeid() typeid()来推断一个类型,但是有时候 t y p e i d typeid typeid不够准确,因此,我们借助 B o o s t Boost Boost库里面的 t y p e _ i d _ w i t h _ c v r < > type\_id\_with\_cvr<> type_id_with_cvr<>来帮助推断。


关于 B o o s t Boost Boost库的安装可以参考这篇文章:Boost库的安装

安装、配置完成后,写入以下代码,编译成功说明配置正确了。

#include<iostream>
#include<boost/type_index.hpp>

template<typename T>
void myfunc1(const T& tmprv) {
	std::cout << "-----------------begin-----------------\n";

	using boost::typeindex::type_id_with_cvr;

	std::cout << "T = " << type_id_with_cvr<T>().pretty_name() << "\n";
	std::cout << "tmprv = " << type_id_with_cvr<decltype(tmprv)>().pretty_name() << "\n";

	std::cout << "-----------------end-----------------\n";
	
}

二、函数模板类型推断

2.1 引用类型

如果参数类型时引用,但不是万能引用时,我们看下面的推断代码:

2.1.1 普通引用类型

//引用类型
template<typename T>
void myfunc2(T& tmprv) {
	std::cout << "-----------------begin-----------------\n";

	using boost::typeindex::type_id_with_cvr;

	std::cout << "T = " << type_id_with_cvr<T>().pretty_name() << "\n";
	std::cout << "tmprv = " << type_id_with_cvr<decltype(tmprv)>().pretty_name() << "\n";

	std::cout << "-----------------end-----------------\n";

}

void Test2() {
	int i = 18; //T = int , tmprv = int &
	const int j = i;//T = int const, tmprv  = const int&
	const int& k = i;//T = int const, tmprv  = const int&

	myfunc2(i);
	myfunc2(j);
	myfunc2(k);
}

推断结果如下:

在这里插入图片描述


2.1.2 c o n s t const const修饰的引用类型

如果形参上带有 c o n s t const const,并且有引用类型呢,我们看下面的代码:

template<typename T>
void myfunc3(const T& tmprv) {
	std::cout << "-----------------begin-----------------\n";

	using boost::typeindex::type_id_with_cvr;

	std::cout << "T = " << type_id_with_cvr<T>().pretty_name() << "\n";
	std::cout << "tmprv = " << type_id_with_cvr<decltype(tmprv)>().pretty_name() << "\n";

	std::cout << "-----------------end-----------------\n";

}

void Test3() {
	int i = 18; //T = int, tmprv = const int &
	const int j = i;//T = int , tmprv  = const int&
	const int& k = i;//T = int , tmprv  = const int&

	myfunc3(i);
	myfunc3(j);
	myfunc3(k);
}

推断结果如下:
在这里插入图片描述


2.2.3 总结和分析

通过简单分析我们发现:

( 1 ) (1) (1)带有引用类型的形参传入后,引用类型会被忽略。
( 2 ) (2) (2)无论形参是否带有 c o n s t const const限定,只有实参具有 c o n s t const const限定,那么推导出来的形参 t m p r v tmprv tmprv就一定具有 c o n s t const const限定


通过上面的范例,总结一下编码技巧。

&①&形参中的引用有两个作用:可以通过对形参的修改来修改实参﹔传递引用比传值效率更高。试想,如果实参是一个对象,那么形参是引用类型可以避免对象的拷贝构造。所以,一般来说,函数模板中的形参建议优先考虑“ T &   t m p r v T\& \ tmprv T& tmprv”这样的形态,就不怕实参中的引用被忽略掉而导致开发者想通过对形参的修改达到修改实参的本意无法达成。

② ② 如果既想享受形参为引用带来的效率上的提高,又不希望通过形参修改实参,则函数模板中的形参建议考虑“ c o n s t   T & const \ T\& const T&”这样的形态。


3.万能引用类型

我们知道,万能引用类型可以接受左值也能接受右值,那么我们现在就看看万能引用中参数的推断情况吧,参考下方代码:

//万能引用类型
template<typename T>
void myfunc5(T&& tmprv) {
	std::cout << "-----------------begin-----------------\n";

	using boost::typeindex::type_id_with_cvr;

	std::cout << "T = " << type_id_with_cvr<T>().pretty_name() << "\n";
	std::cout << "tmprv = " << type_id_with_cvr<decltype(tmprv)>().pretty_name() << "\n";

	std::cout << "-----------------end-----------------\n";

}

void Test5() {
	int i = 10; //T = int, tmprv = int&
	const int j = i;// T = int const, tmprv = int const&
	const int& k = i;//T = int const& , tmprv = int const&
	
	myfunc5(i);
	myfunc5(j);
	myfunc5(k);
	myfunc5(100);//T = int, tmprv = int&&
	int&& x = 10;
	myfunc5(x);
}

参考推断结果:

在这里插入图片描述

可以发现,万能引用可以正确推导出左值和右值的类型,同样会保留形参的 c o n s t const const修饰。
注意这里 m y f u n c ( x ) myfunc(x) myfunc(x)传入的右值引用是左值,因此,推导为 t m p r v = i n t & tmprv = int\& tmprv=int&是没问题的。


4.指针类型

指针类型与引用类型相似,但有细微差异。
参考下方代码:

//指针类型
template<typename T>
void myfunc4(T* tmprv) {
	std::cout << "-----------------begin-----------------\n";

	using boost::typeindex::type_id_with_cvr;

	std::cout << "T = " << type_id_with_cvr<T>().pretty_name() << "\n";
	std::cout << "tmprv = " << type_id_with_cvr<decltype(tmprv)>().pretty_name() << "\n";

	std::cout << "-----------------end-----------------\n";

}

void Test4() {
	int i = 18;
	const int* pi = &i;

	myfunc4(&i); //T = int ,tmprv = int*
	myfunc4(pi);//T = int const, tmprv = int const* 

}

同样是实参存在 c o n s t const const限定,则推断出的形参一定有 c o n s t const const限定

在这里插入图片描述


5. 传值类型

5.1 一般传值方法

形参不带任何修饰 ( c o n s t , ∗ , & (const,*,\& (const,,&…),那么就称为传值类型:
参考下方代码:

void Test6() {
	int i = 18; //T =int , tmprv = int 
	const int j = i;//T = int, tmprv = int;
	const int& k = i; //T = int, tmprv = int;

	myfunc6(i);
	myfunc6(j);
	myfunc6(k);
}

推断结果:
在这里插入图片描述


可以发现:
( 1 ) (1) (1)如果实参具有 c o n s t const const限定,那么 c o n s t const const限定会被忽略。

( 2 ) (2) (2)如果实参为引用,那么引用也会被忽略。


5.2 显式调用模板

不过,如果使用显式的调用模板,可以推导出 c o n s t const const限定或 & \& &引用类型,当然一般情况下,不推荐这样写:

//显式指定推导为引用
myfunc6<const int&>(k); //T = int const&, tmprv = int const&
int& m = i;
myfunc6<int&>(m);//T = int&, tmprv = int&

推断结果:

在这里插入图片描述


5.3 传入指针

当我们向传值模板中传入常量指针,那么会有一个细节,参考下方代码:

	//如果是指针
	char mystr[] = "I love C++ !"; 
	const char* p = mystr; //T = char const* , tmprv = char const*
	myfunc6(p);

在模板中加入以下语句:

//增加修改语句
tmprv = 0;
*tmprv = 'Y'; //指针指向的内容不可被修改

其中 t m p r v = 0 tmprv = 0 tmprv=0合法,而 ∗ t m p r v = ′ Y ′ *tmprv ='Y' tmprv=Y非法,我们可以看看下面的推断结果:

在这里插入图片描述可以发现,指针的限定 c o n s t const const被忽略了,但是指针指向的数组的常量性会被保留,这是其特殊的一面。


5.4 传值的引申

如果我们想在传值方法中传入引用或者 c o n s t const const限定,除了显式的调用模板(不推荐)
,我们也可以使用标准库中的 s t d : : r e f std::ref std::ref s t d : : c r e f std::cref std::cref,分别对应了 & \& & c o n s t   & const \ \& const &

具体来说,模板实际上是被推断为 s t d : : r e f e r e n c e _ w r a p p e r < > std::reference\_wrapper<> std::reference_wrapper<>类型和其的一个 c o n s t const const限定版本,而 s t d : : r e f std::ref std::ref可以隐式地转换为 & \& &类型。

参考下方代码:

//传值对象的引申
template<typename T>
void myfunc7(T tmprv) {
	std::cout << "-----------------begin-----------------\n";

	using boost::typeindex::type_id_with_cvr;

	std::cout << "T = " << type_id_with_cvr<T>().pretty_name() << "\n";
	std::cout << "tmprv = " << type_id_with_cvr<decltype(tmprv)>().pretty_name() << "\n";

	std::cout << "-----------------end-----------------\n";

	//tmprv = 12; //错误,std::reference_wrapper<T>没有赋值运算符

	int& tmpvaluec = tmprv; //正确,隐式转换为引用类型
	tmpvaluec = 0; //修改tmprv
}

void Test7() {
	int i = 100;
	myfunc7(std::ref(i)); //推导为std::reference_wrapper<int>类型
	//T = class std::reference_wrapper<int>
	tmprv = class std::reference_wrapper<int>
	std::cout << i << "\n";
	
	//无法被修改
	const int j = 100;
	myfunc7(std::cref(j));//推导为std::reference_wrapper<const int>类型
	//T = class std::reference_wrapper<int const >
	tmprv = class std::reference_wrapper<int const >
	std::cout << j << "\n"; 

}

值得注意的是, s t d : : r e f e r e n c e _ w r a p p e r < i n t > std::reference\_wrapper<int> std::reference_wrapper<int>类内没有重载 o p e r a t o r = operator = operator=,因此无法被显式地赋值,但是可以使用 i n t & int\& int&来隐式地转换,然后修改 i n t & int\& int&的值,从而达到修改的目的。

同样的,通常 s t d : : r e f std::ref std::ref包装的类型通常为值,如 i n t , l o n g , d o u b l e int,long,double int,long,double等没有修饰的类型,而 s t d : : c r e f std::cref std::cref包装的类型为 c o n s t   i n t , c o n s t   l o n g const \ int, const\ long const int,const long等含有 c o n s t const const限定的类型。


6.函数名作为实参

这里讨论的函数名是全局函数,即函数指针,而如匿名函数、成员函数等暂时不讨论。

参考下方代码:


//函数名作为实参
void testFunc() {}

void Test9() {
	myfunc8(testFunc); //传值
	//T = void (__cdecl*)(void)
	//tmprv = void(__cdecl*)(void)
	
	myfunc2(testFunc); //传引用
	//T = void __cdecl(void)
	//tmprv = void(__cdecl&)(void)
}

这里,我们分别尝试对函数指针进行值传递,引用传递和指针传递,推导结果如下:
在这里插入图片描述
可以发现:
值传递的模板推导出了指针 ( ∗ ) (*) ()。引用传递的模板推导为了函数引用, v o i d ( & ) ( v o i d ) void(\&)(void) void(&)(void),忽略了指针。而指针传递的模板由于存在 ∗ * ,因此直接忽略了指针。


7.数组作为实参

数组和指针类似,有时会被当做地址被传递,这里我们分别尝试将数组按值传递和按引用传递,参考下方代码:

//数组作为实参
void Test8() {
	const char mystr[] = "I Love C++ !"; 
	myfunc8(mystr); //传值
	//T = char const * 
	//	tmprv = char const* 

	myfunc2(mystr);//传引用
	//T = char const [13]
	//tmprv = char const (&__ptr64)[13]

}

推断结果如下:
在这里插入图片描述
我们发现:

在值传递版本中,数组直接被推断为了指针,因此就被当做了指针传递
在引用传递版本中,数组被推断为了数组引用,包含了数组的大小 [ 13 ] [13] [13],按引用传递保持了数组自身的性质,而不会退化为指针。


8.初始化列表作为实参

要使用初始化列表作为实参,需要加入头文件: # i n c l u d e < i n i t i a l i z e r l i s t > \#include<initializer_list> #include<initializerlist>,下面我们使用初始化列表进行传值和传引用,参考下方代码:

//初始化列表作为实参
void Test10() {
	myfunc10({ 1,2,3,4 }); //传值
	//T = int
	//tmprv = class std::initializer_list<int>
	
	std::initializer_list<int> list = { 1,2,3,4 };
	myfunc11(list); //传引用
	//T = int
	//tmprv = class std::initializer_list<int> &

}

可以发现,初始化列表正常的被推断为了值版本和引用版本:

在这里插入图片描述

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

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

相关文章

yolo车位数据集

停车场车位检测数据集是一个非常有价值的数据资源&#xff0c;它对于开发和训练能够自动识别停车位是否被占用的计算机视觉系统至关重要。以下是对这样一个数据集的详细介绍&#xff0c;以及如何使用这个数据集来训练YOLO&#xff08;You Only Look Once&#xff09;这样的目标…

springcloud整合nacos、sentinal、springcloud-gateway,springboot security、oauth2总结

源码地址:下载地址 使用该架构的项目地址:下载地址 下面教大家整合nacos、sentinal、springcloud-gateway,springboot security、oauth2做一个分布式架构 1、第一步整合nacos 1、下载alibaba的nacos 下载地址&#xff0c;然后使用单机模式启动nacos sh startup.sh -m standalon…

游戏如何检测加速外挂

在游戏面临的众多外挂风险中&#xff0c;除了常见的内存修改挂、注入挂等作弊手段&#xff0c;黑灰产还常用「加速」手段实现作弊。 游戏安全风险分布占比图 「加速」顾名思义是指改变游戏内的速度。游戏在运行中需要以帧为单位播放画面&#xff0c;而计算每帧动画播放所需时间…

游戏如何应对云手机刷量问题

云手机的实现原理是依托公有云和 ARM 虚拟化技术&#xff0c;为用户在云端提供一个安卓实例&#xff0c;用户可以将手机上的应用上传至云端&#xff0c;再通过视频流的方式&#xff0c;远程实时控制云手机。 市面上常见的几款云手机 原本需要手机提供的计算、存储等能力都改由…

深度图变换器的新突破:DeepGraph

人工智能咨询培训老师叶梓 转载标明出处 在图变换器领域&#xff0c;尽管其全局注意力机制在图结构数据处理上显示出了巨大潜力&#xff0c;但现有的图变换器模型却普遍较浅&#xff0c;通常不超过12层。这一现象引发了学者们对于“增加层数是否能进一步提升图变换器性能”的深…

Vue3 多组复选框重置(v-if 强制刷新组件)

通过v-if指令强制刷新&#xff0c;当v-if的值发生变化时&#xff0c;组件都会被重新渲染一遍。因此&#xff0c;利用v-if指令的特性&#xff0c;可以达到强制刷新组件的目的。 先用个简单例子 --> 项目中使用 <template><comp v-if"refresh"></c…

普罗米修斯监控

目录 概念 部署方法 1. 二进制&#xff08;源码包&#xff09; 2. 部署在k8s集群当中&#xff0c;用pod形式部署 概念 prometheus是开源的系统监控和告警。在k8s分布式的容器化管理系统当中&#xff0c;一般都是搭配prometheus来进行监控。它是服务监控系统&#xff0c;也…

运动规划第二节【深蓝学院,高飞】笔记

文章目录 Graph Search BasisConfiguration SpaceConfiguration Space ObstacleWorkspace and Configuration Space Obstacle Graph and Search MethodGraph Search OverviewGraph TraversalBreadth First Search (BFS)Depth First Search (DFS)versus Heuristic SearchGreedy …

武汉大学:如何做好高校电子邮件账号安全防护

上个世纪七十年代&#xff0c;电子邮件占据了互联网的前身ARPANET上流量的75%&#xff0c;是最主要的应用。随着互联网的发展&#xff0c;电子邮件在全面普及后&#xff0c;被各种各样的即时通讯软件抢走了不少风头。然而&#xff0c;其始终还是被社会所认可的主流网络通讯渠道…

网络高级day01(Modbus 通信协议)

目录 1》modbus分类 1> Modbus RTU 2> Modbus ASCLL 3> Modbus TCP 2》Modbus TCP的特点 3》Modbus TCP协议 1> 报文头&#xff08;一共7个字节&#xff09; 2> 寄存器 3> 功能码 4> 数据 01H 功能码分析 05H 功能码分析 0FH 功能码分析 1》modbus…

git reflog 和 git log 的详解和区别

文章目录 1. git log 介绍基本用法&#xff1a;输出内容&#xff1a;常见选项&#xff1a;git log 的局限性&#xff1a; 2. git reflog 介绍基本用法&#xff1a;输出内容&#xff1a;git reflog 输出字段&#xff1a;常见选项&#xff1a;主要用途&#xff1a;示例&#xff1…

Rasa对话模型——做一个语言助手

1、Rasa模型 1.1 模型介绍 Rasa是一个用于构建对话 AI 的开源框架&#xff0c;主要用于开发聊天机器人和语音助手。Rasa 提供了自然语言理解&#xff08;NLU&#xff09;和对话管理&#xff08;DM&#xff09;功能&#xff0c;使开发者能够创建智能、交互式的对话系统。 1.2…

JavaScript 基础 - 第20天_Node.js模块化

文章目录 Day02_Node.js模块化目录学习目标01.模块化简介目标讲解小结 02.ECMAScript标准-默认导出和导入目标讲解小结 03.ECMAScript标准-命名导出和导入目标讲解小结 04.包的概念目标讲解小结 05.npm软件包管理器目标讲解小结 06.npm安装所有依赖目标讲解小结 07.npm全局软件…

计算机领域CCF推荐期刊A/B/C类全目录

计算机领域CCF推荐期刊 最新在检的【自动化与控制系统】的64本SCI期刊最新影响因子、期刊分区、自引率 CCF-A类 CCF-B类 CCF-C类 更多期刊解析干货&#xff0c;移步公众号【Unionpub学术】 计算机领域CCF推荐期刊A/B/C类全目录&#xff08;附excel下载&#xff09;

3.使用 VSCode 过程中的英语积累 - Selection 菜单(每一次重点积累 5 个单词)

前言 学习可以不局限于传统的书籍和课堂&#xff0c;各种生活的元素也都可以做为我们的学习对象&#xff0c;本文将利用 VSCode 页面上的各种英文元素来做英语的积累&#xff0c;如此做有 3 大利 这些软件在我们工作中是时时刻刻接触的&#xff0c;借此做英语积累再合适不过&a…

多线程篇六

多线程篇六 如笔者理解有误欢迎交流指正~⭐ 什么是单例模式&#xff1f; 单例模式是最常见的 设计模式. 顾名思义&#xff0c;单例模式指的就是单个实例的模式.&#xff08;针对某些类只能使用一个对象的场景【如MySQL、JDBC、DataSource】&#xff09; 设计模式 设计模式是…

CentOS7.9环境上NFS搭建及使用

Linux环境NFS搭建及使用 1. 服务器规划2. NFS服务器配置2.1 主机名设置2.2 nfs安装2.2.1 repo文件替换2.2.2 NFS服务安装 2.3 nfs配置2.4 服务查看2.5 资源发布2.6 配置nfs服务开机自启2.7 端口开放 3.应用服务器配置3.1 主机名设置3.2 nfs安装3.2.1 repo文件替换3.2.2 NFS服务…

Vue学习记录之五(组件/生命周期)

一、组件 在每一个.vue文件可以看作是一个组件&#xff0c;组件是可以复用的&#xff0c;每个应用可以看作是一棵嵌套的组件树。 在Vue3中&#xff0c;组件导入以后即可直接使用。 二、组件的生命周期 生命周期就是从诞生(创建)到死亡(销毁) 的过程。 Vue3 组合式API中(se…

Java中的事务管理

1.1 事务管理 1.1 事务回顾 事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位。事务会把所有的操作作为一个整体&#xff0c;一起向数据库提交或者是撤销操作请求。所以这组操作要么同时成功&#xff0c;要么同时失败。 怎么样来控制这组操作&#xff0c;让这组操…

力扣最热一百题——合并两个有序链表

目录 题目链接&#xff1a;21. 合并两个有序链表 - 力扣&#xff08;LeetCode&#xff09; 题目描述 示例 提示&#xff1a; 解法一&#xff1a;比大小放入 Java写法&#xff1a; 运行时间以及复杂度 C写法&#xff1a; 运行时间以及复杂度 总结 题目链接&#xff1a…