《C++ Templetes》《1、函数模板》

文章目录

  • 1、函数模板
    • 1.1 函数模板的简单示例:
    • 1.2 模板参数
    • 1.3 实参演绎
      • 实参演绎的概念
      • 实参演绎的过程
      • 注意事项
      • 示例
    • 1.4 函数模板的重载
  • 2、总结
  • 3、参考

在《 Effective C++》中第一个建议就是,把C++看作是一个语言的联邦,其中Templates在联邦中留下了浓墨重彩的一笔。首先说一下为什么需要使用模板;在C语言中是没有模板这个概念的,C中如果声明一个函数,或者指定类型,在程序中是不可更改的。如果一个类似的问题,需要一段代码进行处理,除了输入类型不同外,其余的代码都是相同的,这个时候就需要模板来解决这个问题。

1、函数模板

1.1 函数模板的简单示例:

函数模板提供了一种函数行为,这个行为可以用多种不同的类型进行调用。问题还是那个问题,但是解决办法更具有通用性,如果你也有同样的需求,你就能拿过来使用,不受限于你是何种类型。
举一个简单的栗子:

//用来返回两个值中较大的那个的函数模板
template <typename T>
inline T const& max_num(T const& a,T const & b){return a < b ? b : a;
}

上面就是一个简单的模板,通过参数a,b将两个值传递给函数模板;模板参数用T来代替。
Template的的使用方法:
template <用逗号隔开的参数列表>,在上面的例子中,参数类型是T,你可以使用任意标识符作为类型参数的名称,A,B,C,D都可以,但是使用T已经成为了一种惯例。
举一个使用的栗子:

#include <iostream>
#include <string>
using namespace std;
template <typename T>
inline T const& max_num(T const& a,T const& b){return a < b? b:a;
};
int main(){int i = 42,j = 40;cout << "max(i,j) : "<<max_num(i,j) << endl;double d1 = 1.23,d2 = 4.56;cout << "max(d1,d2) : "<<max_num(d1,d2) << endl;string s1 = "hello",s2 = "world";cout << "max_num(s1,s2) : "<<max_num(s1,s2)<<endl;  return 0;
}

//输出内容:

max(i,j) : 42
max(d1,d2) : 4.56
max_num(s1,s2) : world...Program finished with exit code 0
Press ENTER to exit console.

在上面的栗子中,max_num被调用了3次,3次的参数每次都不同,分别是int,double,string。通常而言,模板函数并不是一个可以处理任何类型的单一实体,而是对于实例化模板参数的每种类型,都从模板产生出一个不同的实体。
在传入int参数时,声明和实现会自动帮你生成出来:

const int& max_num(int const&,int const&);
inline int const& max_um(int const& a,int const& b){return a<b?b:a;
}

也就是说,他会帮你将具体类型代替模板参数,这个过程叫做实例化。
如果,函数模板本身出问题,或者调用了不支持模板内部使用的参数,程序编译都会报错。这说明模板被编译了2次;
1、实例化之前:检查模板本身是否有语法错误;
2、实例化期间:产看是否所有调用都有效;

1.2 模板参数

函数模板有2种类型的参数:
(1)模板参数:位于函数模板名称前面,在尖括号内声明

template <typename T>

(2)调用参数:位于函数模板名称之后,在圆括号内声明

max_num(T const& a,T const& b)

在实际中,也有可能出现不同类型的情况,但是模板是死脑筋,如果是int,double,这两种不同的参数类型,模板是不允许进行自动类型转换的。
举个栗子:

max_num(4,5) 		//返回其中较大的数值
max_num(4,4.2) 		//出错

这个问题,有3种常规的解决办法:

  • a.对实参进行强制类型转换
max_num(static_cast<double>(4),4.2)
  • b.显式地指定T的类型
max<double>(4,4.2)
  • c.在声明和实现中,让2个参数具有不同的类型;
//指定返回T1类型
template <typename T1,typename T2>
inline T1  max_num(T1 const& a,T2 const& b){return a < b? b:a;
};
...
max_num(4,4.2);

这个与之前,增加了一个参数类型,但是把这2个参数转型为返回类型的过程会创建一个新的局部临时对象,这样就不能返回引用,只能返回T1。

  • d.引入额外的参数类型
template<typename T1,typename T2,typename RT>
inline RT max_num(T1 const& a,T2 const& b);
//明确指定各个参数的类型;
max_num<int,double,double>(4,4.2);
//只指定返回的类型,也可;
max_num<double>(4,4.2)

1.3 实参演绎

在C++中,模板(Templates)是一种强大的泛型编程工具,它允许程序员在编写代码时使用类型参数,从而使代码更加通用和灵活。模板可以分为函数模板和类模板两种类型。而实参演绎(Argument Deduction)是函数模板中一个非常关键的概念,它指的是编译器根据函数模板调用时提供的实际参数(实参)的类型来推断模板参数的类型和值的过程。

实参演绎的概念

实参演绎的字面意思就是:通过实参的类型来推断函数模板的类型参数。在C++中,当函数模板被调用时,编译器会根据传递给模板函数的实参类型自动推断出模板参数的具体类型,从而生成一个特定类型的函数实例。这个过程就是实参演绎。

实参演绎的过程

实参演绎的过程涉及编译器如何根据实参的类型来确定模板参数的类型。具体来说,包括以下几个关键步骤:

  1. 匹配实参和模板参数:编译器首先会尝试将函数模板调用中的实参与模板参数进行匹配。如果实参的类型可以直接与模板参数的类型相对应,则匹配成功。

  2. 类型转换:在某些情况下,如果实参的类型与模板参数的类型不完全匹配,编译器会尝试进行类型转换。例如,从数组类型到指针类型的转换,或从函数类型到函数指针类型的转换。

  3. 推导模板参数类型:根据实参的类型和可能发生的类型转换,编译器会推导出模板参数的具体类型。

  4. 实例化函数模板:一旦模板参数的类型被确定,编译器就会使用这些类型来实例化函数模板,生成一个具体的函数实例。

注意事项

  1. 模板参数的可推导性:并非所有的模板参数都可以通过实参演绎来推导。有些模板参数可能需要显式指定,特别是在模板参数没有出现在函数参数列表中的情况下。

  2. 类型转换的限制:虽然编译器会尝试进行类型转换来匹配实参和模板参数,但这些转换通常受到语言规则的限制。例如,不能将数组直接传递给需要指针参数的函数模板,但可以将数组名隐式转换为指向数组首元素的指针。

  3. 重载解析:当存在多个重载的函数模板时,编译器需要根据实参的类型和模板参数的推导结果来选择最合适的函数模板进行实例化。

示例

以下是一个简单的函数模板示例,展示了实参演绎的过程:

template<typename T>
T max(T a, T b) {return (a > b) ? a : b;
}int main() {int i = max(1, 2);  // 实参演绎:T被推导为intdouble d = max(1.0, 2.0);  // 实参演绎:T被推导为doublereturn 0;
}

在这个示例中,编译器根据传递给max函数的实参类型自动推导出了模板参数T的类型,并生成了相应的函数实例。

综上所述,C++模板中的实参演绎是一个自动且强大的机制,它允许程序员编写更加通用和灵活的代码,同时减少了代码重复和提高了开发效率。

1.4 函数模板的重载

和普通函数一样,函数模板可以被重载,根据传入参数类型的个数,选择不同的实现,将上面的l例子进行延伸:

/******************************************************************************Welcome to GDB Online.GDB online is an online compiler and debugger tool for C, C++, Python, PHP, Ruby, C#, OCaml, VB, Perl, Swift, Prolog, Javascript, Pascal, COBOL, HTML, CSS, JSCode, Compile, Run and Debug online from anywhere in world.*******************************************************************************/
#include <iostream>
#include <string>
using namespace std;
inline int const& max_num(int const& a,int const& b){return a < b ? b : a;
}template <typename T>
inline T const& max_num(T const& a,T const& b){return a < b ? b : a;
};template <typename T>
inline T const& max_num(T const& a,T const& b,T const& c){return max_num(max_num(a,b),c);
};int main(){cout << "max_num(1,2,3) : "  << max_num(1,2,3)<<endl;  cout << "max_num(1.0,2.0) : " << max_num(1.0,2.0) << endl;  cout << "max_num('a','b') : " << max_num('a','b') << endl;  cout << "max_num(1,2) : " << max_num(1,2) << endl;  cout << "max_num<>(1,2) : " << max_num<>(1,2) << endl;  cout << "max_num<double>(1,2) : "   <<  max_num<double>(1,2) << endl;  cout << "max_num('a',1.2) : "  <<  max_num('a',1.2) << endl;  return 0;
}
max_num(1,2,3) : 3         //3个参数的模板
max_num(1.0,2.0) : 2       //max_num<double>
max_num('a','b') : b       //max_num<char>
max_num(1,2) : 2.          //非模板函数(优先调用)
max_num<>(1,2) : 2         //调用模板函数
max_num<double>(1,2) : 2.  //调用max_num<double>
max_num('a',1.2) : 97      //类型不同,只能调用非模板函数
...Program finished with exit code 0
Press ENTER to exit console.

补充说明:
(1)重载函数模板的时候,要把自己的改变限制在:显式地指定模板参数;
(2)一定要让函数模板的所有重载版本的声明都位于它们被调用的位置之前;
再举一个例子,为指针和普通的C字符串重载,求最大值的模板。

#include <iostream>
#include <string>
#include <cstring>
using namespace std;template <typename T>
inline T const& max_num(T const& a,T const& b){return a < b ? b : a;
};//求2个指针所指向值的最大值
template <typename T>
inline T* const& max_num(T* const& a,T* const& b){return *a < *b ? b : a;
};//求2个C字符串的最大值
template <typename T>
inline char const* const& max_num(char const* const& a,char const* const& b){return std::strcmp(a,b) < 0 ? b : a;
};int main(){int a = 7;int b = 42;cout << "max_num(a,b): " << max_num(a,b) << endl;std::string s = "hey";std::string t = "you";cout << "max_num(s,t): " << max_num(s,t) << endl;int* p1 = &b;int* p2 = &a;cout << "max_num(p1,p2): " << max_num(p1,p2) << endl;char const* s1 = "aaa";char const* s2 = "bbb";cout << "max_num(s1,s2): " << max_num(s1,s2) << endl;return 0;
}
#include <iostream>
#include <string>
#include <cstring>
using namespace std;//通过传引用进行调用        
template <typename T>
inline T const& max_num(T const& a,T const& b){return a < b ? b : a;
};//求2个C字符串的最大值(通过传值进行调用)
template <typename T>
inline T const& max_num(char const*  a, char const* b){return std::strcmp(a,b) < 0 ? b : a;
};//求三个任意类型的最大值
template <typename T>
inline T const& max_num(T const& a, T const& b,T const& c){return max_num(max_num(a,b),c);
};int main(){int a = 7;int b = 42;int c = 61;cout << "max_num(a,b,c): " << max_num(a,b,c) << endl;  //okchar const* s1 = "aaa";char const* s2 = "bbb";char const* s3 = "ccc";cout << "max_num(s1,s2,s3): " << max_num(s1,s2,s3) << endl;  //errorreturn 0;
}

为什么第二个会出错,是因为对于c-string而言,max(a,b)产生了一个新的临时局部变量,这个值有可能会被外面的max_num函数以传引用的方式返回,这将导致传回无效的引用。
所以这两个例子给我们的启示:所有重载的实现里面,我们都是通过引用来传递每个实参的;通常情况下,在重载函数模板时,最好改变那些需要改变的内容,即改变参数的数目,或者显示地指定模板参数

2、总结

  • 模板函数为不同的模板实参定义了一个函数家族;
  • 当你传递模板实参的时候,可以根据实参的类型来对函数模板进行实例化;
  • 可以显示地指定模板参数;
  • 可以重载函数模板;
  • 当你重载函数模板的时候,应该把改变限制在:显示地指定模板参数;
  • 一定要让函数模板的所有重载版本的声明位于函数的调用位置之前(先声明再调用)

3、参考

3.1 《C++ Templates》

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

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

相关文章

多元统计分析概述

目录 1. 多元回归分析 2. 主成分分析&#xff08;PCA&#xff09; 3. 因子分析 4. 判别分析 5. 聚类分析 6. 多维尺度分析&#xff08;MDS&#xff09; 结论 多元统计分析是一组用于分析多个变量之间关系的统计方法。它广泛应用于各个领域&#xff0c;如市场研究、生物医…

华为OD机试D卷 --可以处理的最大任务数--24年OD统一考试(Java JS Python C C++)

文章目录 题目描述输入描述输出描述用例1题目解析java源码js源码c++源码题目描述 在某个项目中有多个任务(用task数组表示)需要你进行处理,其中: task[i] = [si, ei] 你可以在 si ≤ day ≤ ei 中的任意一天处理该任务,请返回你可以处理的最大任务数。 输入描述 第一行…

PHP webshell 免杀方法

本文介绍php类webshell简单的免杀方法&#xff0c;总结不一定全面&#xff0c;仅供读者参考。 webshell通常可分为一句话木马&#xff0c;小马&#xff0c;大马&#xff0c;内存马。 一句话木马是最简单也是最常见的webshell形式&#xff0c;这种木马体积小&#xff0c;隐蔽较…

a newer or same version is present nvidia解决方案

安装时候出现a newer or same version is present nvidia 或者Night Visual Editor 失败&#xff0c;把显卡驱动卸载掉&#xff0c;打开service.mtc 服务控制面板&#xff0c;把nvidia开头的服务全停掉&#xff0c;重新启动cuda安装程序选择自定义安装 vse visual studio相关的…

学懂C#编程:常用框架学习(三)——.NET Framework框架下的Entity Framework (EF)开发应用详解

目录 一、Entity Framework概述 二、Entity Framework的核心组成部分 Entity Data Model (EDM)&#xff1a; Entity Client&#xff1a; Object Services&#xff1a; ADO.NET Provider&#xff1a; 三、分层结构 应用程序层&#xff1a; Entity Framework层&#xff…

鸿蒙语言基础类库:【@system.device (设备信息)】

设备信息 说明&#xff1a; 从API Version 6开始&#xff0c;该接口不再维护&#xff0c;推荐使用新接口[ohos.deviceInfo]进行设备信息查询。本模块首批接口从API version 3开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import dev…

linux操作fb文件节点刷纯色

代码 #include <unistd.h> #include <stdio.h> #include <fcntl.h> #include <linux/fb.h> #include <sys/mman.h> #include <stdlib.h> #include <string.h>#define RED 0xF800 #define YELLOW 0xFFE0 #define BLUE 0x0…

手把手教你,如何利用积木易搭3D扫描仪完成文物三维建模?

当前&#xff0c;文物三维建模主要技术手段主要有摄影测量技术、三维激光扫描技术、结构光扫描技术。其中&#xff0c;积木易搭的MagicScan作为一款先进的3D扫描仪&#xff0c;是正是运用了结构光扫描技术的精髓&#xff0c;它巧妙地融合了点云相机的高精度空间数据采集能力、纹…

如何用python写接口

如何用python写接口&#xff1f;具体步骤如下&#xff1a;  1、实例化server 2、装饰器下面的函数变为一个接口 3、启动服务 开发工具和流程&#xff1a; python库&#xff1a;flask 》实例化server&#xff1a;server flask.Flask(__name__) 》server.route(/index,met…

【STM32嵌入式系统设计与开发---拓展】——1_9 GPIO的输入和输出

这里写目录标题 0、输入 输出1、输出&#xff08;1&#xff09;GPIO_SetBits:用于设置 GPIO 引脚的状态&#xff08;即将指定的引脚设置为高电平&#xff09; 2、输入&#xff08;1&#xff09;GPIO_ReadInputDataBit&#xff08;&#xff09; 0、输入 输出 咋们定义了一个结构…

新手程序员如何判断自己的代码是高质量还是烂代码?

“五年工作经验&#xff0c;代码质量却不如三年程序员&#xff1f;” 这句话或许有些夸张&#xff0c;却也反映出代码质量参差不齐的现状。 那么&#xff0c;究竟什么是高质量的代码&#xff1f;如何才能写出让同行称赞、让机器流畅运行的代码呢&#xff1f; 今天我们就来聊…

服务器网络配置

后来呀&#xff0c;天亮之前毕业后踏入服务器领域了。。。。。。 服务器网络配置大体流程&#xff1a;硬件选择——系统安装——驱动调用——网卡配置 1、硬件选择 驱动程序是操作系统与硬件之间通信的桥梁 服务器硬件硬件选择首先看是否还有预留槽位&#xff0c;或者riser…

资产报废 BAPI_ASSET_RETIREMENT_POST

入参BUKRS公司代码ANLN1主资产号ANLN2资产子编号 BUDAT转资日期ANBTR已过账的金额BLDAT凭证中的凭证日期BZDAT资产价值日出参BUKRS公司代码ANLN1主资产号ANLN2资产子编号 BELNR会计凭证号码GJAHR会计年度ZBFJZ按本位币计的金额MSGTS消息类型TEXT消息文本 METHOD assets_retirem…

解决ESLint和Prettier冲突的问题

在配置了ESLint的项目中使用Prettier进行格式化可能会出现冲突&#xff0c;不如Prettier配置了使用双引号&#xff0c;ESLint配置了单引号&#xff0c;当然可以一个一个改成一样的配置&#xff0c;但是比较麻烦。我发现可以直接使用ESLint的规则进行格式化。在VSCode配置过程如…

xss复习总结及ctfshow做题总结xss

xss复习总结 知识点 1.XSS 漏洞简介 ​ XSS又叫CSS&#xff08;Cross Site Script&#xff09;跨站脚本攻击是指恶意攻击者往Web页面里插入恶意Script代码&#xff0c;当用户浏览该页之时&#xff0c;嵌入其中Web里面的Script代码会被执行&#xff0c;从而达到恶意攻击用户的…

软考从报考到拿到证书的过程

一、报考阶段及参加考试阶段 这个阶段可以留意软考官网的的信息&#xff0c;一般上半年是3月份报名、5月份考试&#xff1b;下半年是8月份报名、11月份考试。以下是这个阶段会用到的链接&#xff0c;保持关注就好。 软考官网&#xff1a;中国计算机技术职业资格网 (ruankao.o…

六、 SpringBoot 配置⽂件 ★ ✔【value的引号注意事项、@ConfigurationProperties 、】

六、 SpringBoot 配置⽂件 本节⽬标1. 配置⽂件作⽤2. 配置⽂件快速⼊⼿3. 配置⽂件的格式4. properties 配置⽂件说明4.1 properties 基本语法4.2 读取配置⽂件4.3 properties 缺点分析 5. yml 配置⽂件说明5.1 yml 基本语法5.2 yml 使⽤进阶5.2.1 yml 配置不同数据类型及 nul…

Git简要笔记

Git是一个分布式版本控制系统&#xff0c;用于跟踪文件的变化并协调多人协作开发。它提供了一种管理和追踪代码变化的方式&#xff0c;以便团队成员可以同时进行工作&#xff0c;轻松地合并和管理他们的代码。 Git的基本概念包括&#xff1a; 仓库&#xff08;Repository&…

【GD32】从零开始学GD32单片机 | WDGT看门狗定时器+独立看门狗和窗口看门狗例程(GD32F470ZGT6)

1. 简介 看门狗从本质上来说也是一个定时器&#xff0c;它是用来监测硬件或软件的故障的&#xff1b;它的工作原理大概就是开启后内部定时器会按照设置的频率更新&#xff0c;在程序运行过程中我们需不断地重装载看门狗&#xff0c;以使它不溢出&#xff1b;如果硬件或软件发生…

Vue实现滚动元素始终固定在最底部

1. 应用场景——聊天 在聊天的时候&#xff0c;展示聊天内容的元素是可以滚动的&#xff0c;通过上下滚动来查看过往消息。不过在首次打开聊天页面以及发送新消息时需要固定在滚动的最底部以及时展示最新的消息&#xff0c;这样才能获得比较好的用户体验。 效果&#xff1a; …