C++开发基础——函数模板

一,函数模板

1.基础概念

模板编程是C++中泛型编程的基础。

一个模板可以是创建类或者函数的蓝图。

模板编程分两种,分别是算法抽象的模板、数据抽象的模板。算法抽象的模板以函数模板为主,数据抽象的模板以类模板为主。

基于函数模板生成的函数定义被称为模板的一个实例。

模板的定义以关键字template开始,后跟一个由尖括号"<>"括起来的模板参数列表。

2.函数模板的简单样例

函数模板的开头:template

定义模板参数的关键字:typename

模板参数样例:T1, T2

函数参数样例:a, b

template <typename T1, typename T2>
void func(T1 a, T2 b)
{//process code
}

补充:在C++98标准添加关键字typename之前,C++也可以使用关键字class来为函数模板创建模板参数列表。

代码样例:

template <class T>
void Swap(T &a, T &b)
{T temp;temp = a;a = b;b = temp;
}

3.函数模板的实例化

函数模板的实例化是指,编译器根据函数模板和具体的数据类型生成函数定义。

函数模板在实例化以后,模板参数会变成具体的数据类型,比如int, char等。

对于某一种具体的数据类型,比如int,无论以这个数据类型调用多少次函数模板,最后只生成一次该类型的模板实例。

所以,对于相同的数据类型,第一次调用函数模板的时候才会生成实例,后面再次调用的时候,都是直接使用该实例。

当编译器遇到一个函数模板的定义时,并不会马上生成相关代码,只有当我们将函数模板实例化成一个函数定义时,编译器才会生成代码。

代码样例:

a.函数模板

template <typename T>
T add(T num1, T num2) {return (num1 + num2);
}

b.函数模板的实例化

int result1 = add<int>(2, 3);
double result2 = add<double>(2.2, 3.3);

实例化过程的图示:

 在项目工程中,我们通常将类的定义放在头文件中,将类的成员函数的定义放在源文件中,将普通函数的声明放在头文件中,将普通函数的定义放在源文件中,但是函数模板的规则和它们不一样。

    为了让编译器为实例化后的函数模板生成代码,编译器需要同时知道函数模板的声明和定义,因此函数模板的定义也需要放在头文件中。

4.函数模板的引用传参

 对于以下函数模板:

template <typename T>
T larger(T a, T b)
{return a > b ? a : b;
}

该函数模板实例化以后生成的函数,需要按值传递的方式接收实参。

由于按值传送对象,会导致不必要地复制这些对象,因此,推荐使用const引用的方式定义模板参数。

template <typename T>
const T& larger(const T& a, const T& b)
{return a > b ? a : b;
}

5.函数模板的返回类型推断

对于无返回值的函数模板,可以把返回值类型写为void,比如最开始提到的:

template <typename T1, typename T2>
void func(T1 a, T2 b)

有的函数模板,返回值类型和参数一致,同为T,比如:

template <typename T>
T larger(T a, T b)

但是,当返回值类型和参数不一致时,得想办法让编译器可以推断返回值类型。

最简单的方式是使用auto关键字。

template <typename T1, typename T2>
auto larger(const T1& a, const T2& b)
{return a > b ? a : b;
}

但是,使用auto来推导函数的返回值类型时,会默认去掉引用和const限定符,因此,以上方式会导致返回值发生不必要的复制。

        因此,为了让返回值被const修饰,且采取引用的方式来传值,需要显式地加上"const &",以上代码可以改为: 

template <typename T1, typename T2>
const auto& larger(const T1& a, const T2& b)
{return a > b ? a : b;
}

还有一种更好的方式,C++11标准引入了decltype关键字,decltype相当于"const auto&",因为decltype在做类型推导时,不会去掉引用和const限定符。

但是decltype的用法不能像auto一样,直接放在函数名前面。

decltype用法分两种:

方式1.拖尾方式:decltype(返回值相关代码)

template <typename T1, typename T2>
auto larger(T1 a, T2 b) -> decltype(a > b ? a : b)
{return a > b ? a : b;
}

方式2.和auto关键字结合:decltype(auto)

template <typename T1, typename T2>
decltype(auto) larger(T1 a, T2 b)
{return a > b ? a : b;
}

第一种用法需要把返回值相关的代码逻辑重复写一遍,第二种用法更简洁。

6.模板参数可以指定默认值

可以用具体的数据类型为模板参数指定默认值。

例如:当函数经常使用int类型的参数时,指定模板参数的默认值为int。

template <typename T1=int, typename T2>
void func(T1 a, T2 b)

7.非类型的模板参数

模板参数分两种:

1.类型模板参数

2.非类型模板参数

由尖括号"<>"括起来的模板参数列表中,除了可以包含类型模板参数,还可以包含非类型模板参数。

以上提到的"typename T1, typename T2"中的"T1, T2"都属于类型模板参数,而"int n,  float m"中的"n, m"都属于类型模板参数非类型模板参数

类型模板参数经过实例化会变成具体类型。

非类型模板参数经过实例化会变成具体的值。

代码样例: 

应用场景:比较不同长度的字符串字面常量。

函数模板定义了两个非类型模板参数,参数N表示第一个数组的长度,参数M表示第二个数组的长度。

数组采用const和引用的方式传参。

template<int N, int M>
int compare(const char (&p1)[N], const char (&p2)[M])
{return strcmp(p1, p2);
}

非类型模板参数可以使用的数据类型: 

整型,如int、long等
枚举类型
对象类型的引用或指针
函数的引用或指针
类成员的指针

当模板参数列表中,同时有类型模板参数和非类型模板参数时,建议将非类型模板参数写在类型模板参数的前面。

代码样例:

template <int lower, int upper, typename T>
bool is_in_range(const T& value)
{return (value <= upper) && (value >= lower);
}

完整代码样例:

求任意数据类型,任意大小的数组的平均值。

#include <iostream>
template <typename T, int N>
T average(const T(&array)[N])
{T sum{};int i;for (i = 0; i < N; ++i){sum += array[i];}return sum / N;
}
int main()
{double array_1[2]{ 1.1, 2.1 };std::cout << average(array_1) << std::endl;float array_2[]{ 1.0, 2.0, 3.0, 4.0 };std::cout << average(array_2) << std::endl;int array_3[] = { 1, 2, 3, 4 };std::cout << average(array_3) << std::endl;return 0;
}

运行结果:

1.6
2.5
2

8.inline/constexpr修饰的函数模板

和具体函数一样,函数模板可以用inline或constexpr修饰。

inline或constexpr在修饰时放在模板参数列表之后,返回值类型之前。

代码样例:

template <typename T>
inline T min(const T&, const T&);

9.函数模板的重载

函数模板的重载有两种方式:

方式1.用同名函数重载函数模板

方式2.用另一个函数模板重载已有模板

重载的代码样例:

template <typename T>
T larger(const T data[], size_t count)
{T result {data[0]};for (size_t i {1}; i < count; ++i){if (data[i] > result)result = data[i];}return result;
}template <typename T>
T larger(const std::vector<T>& data)
{T result {data[0]};for (auto& value : data){if (value > result)result = value;}return result;
}

二,函数模板的特例 

1.基础概念

函数模板的特例是由原始的函数模板具体化而来的,因此,函数模板的特例也被称为函数模板的具体化(explicit specialization)

函数模板的特例的定义必须放在函数模板的声明和定义之后。

当编译器找到与函数调用匹配的具体化定义时,将直接使用该函数模板的特例,而不再实例化函数模板。

函数模板的特例也以关键字template开头,但要省略参数,所以template后面的尖括号是空的。

函数模板的特例的定义需要传递具体的参数类型。

当函数模板的某个实例,需要被定义一种不同于原始函数模板的行为,就可以使用函数模板的特例去定义。

空的尖括号“<>”表示编译器不需要做类型推导。

函数模板特例的简单样例:

template <>
void func(int a, double b)
{//process code
}

2.代码样例

给定函数模板 larger(T1 a, T2 b)

template <typename T1, typename T2>
decltype(auto) larger(T1 a, T2 b)
{return a > b ? a : b;
}

由于该函数模板不适用于指针数据类型,因此,定义以下函数模板的特例。

函数模板的特例,在代码逻辑中相比原始的函数模板多了解引用操作。

template <>
int* larger<int*>(int* a, int* b)
{return *a > *b ? a : b;//解引用操作是为了让两个指针比较指向的数值而不是地址
}

普通函数,函数模板,函数模板特例的代码形式

//function
void Swap(int& a, int& b);//template prototype
template <typename T>
void Swap(T& a, T& b);//template explicit specialization
template <>
void Swap<int>(int& a, int& b);

3.编译时的匹配优先级

当某个具体的数据类型可以同时匹配上普通函数,函数模板,函数模板的特例时,普通函数的调用优先于函数模板特例,函数模板特例的调用优先于原始函数模板。

三,参考阅读

《C++17入门经典》

《C++ primer》

《深入理解C++11》

https://www.programiz.com/cpp-programming/function-template

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

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

相关文章

13.Python从入门到精通—Python 集合操作与方法概览

13.Python从入门到精通—Python 集合操作与方法概览 Python 集合集合的基本操作1、添加元素2、移除元素3、计算集合元素个数4、清空集合5、判断元素是否在集合中存在 集合内置方法完整列表 Python 集合 在Python中&#xff0c;集合是一种无序、不重复的数据类型。集合通常用于…

MVC接收请求教程

mvc接收各种请求 1-环境搭建 1.1-准备apifox发送请求 1.2-项目搭建 ①创建Web骨架的Maven项目 ​ --打开2023-IDEA &#xff0c;选择New Project ​ --选择Maven Archetype ​ --注意点&#xff1a;Catalog默认就行了 ​ --Archetype选择webapp ​ --JDK跟着黑马敲最好…

无需借助任何三方软件,使用Windows自带分区功能实现磁盘分区!

像上面截图那样,如果当你由于文件分类或磁盘容量,想增加或减少分区盘符该怎么办。简单的理解就是怎么从E盘再分出个F盘,或者怎么从E盘分出点空间把D盘空间增大。 这里就牵扯出一个概念,磁盘分区,大概意思就是把一整个物理磁盘在系统上分出好几块区域,用来存放不同的文件…

【C++练级之路】【Lv.14】二叉搜索树(进化的二叉树——BST)

快乐的流畅&#xff1a;个人主页 个人专栏&#xff1a;《C语言》《数据结构世界》《进击的C》 远方有一堆篝火&#xff0c;在为久候之人燃烧&#xff01; 文章目录 引言一、二叉搜索树介绍二、二叉搜索树的模拟实现2.1 结点2.2 成员变量2.3 默认成员函数2.3.1 constructor2.3.2…

Springboot笔记-01

简化spring应用开发&#xff0c;约定大于配置 简化Spring应用开发的一个框架&#xff1b; 整个Spring技术栈的一个大整合&#xff1b; J2EE开发的一站式解决方案&#xff1b; 优点&#xff1a; 快速创建独立运行的spring项目以及于主流框架集成 使用嵌入式的Servlet容器&#x…

IonQ最新研究突破!引入光量子纠缠以构建量子计算网络

内容来源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 编辑丨慕一 编译/排版丨沛贤 深度好文&#xff1a;700字丨5分钟阅读 2024年2月22日&#xff0c;美国量子计算公司IonQ宣布&#xff0c;公司研究团队已实现可重复地生成与离子纠缠的光子&#…

目标检测——YOLOv4算法解读

论文&#xff1a;YOLOv4&#xff1a;Optimal Speed and Accuracy of Object Detection 作者&#xff1a;Alexey Bochkovskiy, Chien-Yao Wang, Hong-Yuan Mark Liao 链接&#xff1a;https://arxiv.org/pdf/2004.10934.pdf 代码&#xff1a;https://github.com/AlexeyAB/darkne…

如何使用Excel进行设备管理:巡检、维修、保养、备件管理

在现代企业运营中&#xff0c;设备管理是维持生产效率和保障生产安全的关键环节。良好的设备管理包括设备的巡检、维修、保养和备件管理等多个方面。随着信息技术的发展&#xff0c;许多企业开始利用电子化工具来提高设备管理的效率和准确性。其中&#xff0c;Excel作为一款功能…

旅游行业分析及媒体邀约资源汇总

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 酒店旅游行业分析及媒体邀约资源汇总是两个相对独立但又相互关联的领域。下面将分别对这两个方面进行概述。 酒店旅游行业分析 1. 市场概况 市场规模&#xff1a;评估市场的总价值、增长…

react中使用antdesign组件库的table组件实现自定义筛选菜单

需求如下&#xff1a; 这是一个表格的表头信息&#xff0c;实现这几列的筛选。 涉及到的筛选有&#xff1a;自定义选择项&#xff0c;动态生成选择项&#xff0c;自定义筛选菜单展示 1、依据antdesign官方给出的例子实现的&#xff0c;比如第一列&#xff0c;使用的是&#x…

阿里云RDS MySQL与自建MySQL数据库进行主从同步(GTID方式)

1、创建数据库&#xff0c;dump阿里云RDS数据&#xff0c;导入到自建MySQL 阿里云RDS的配置几乎不用动&#xff0c;只需从RDS开一个只读的账号出来。 在ECS服务器上远程导出RDS的数据库&#xff0c;执行下面命令&#xff0c;记得将[]内容替换掉 mysqldump -h http://***.mys…

安装vcenter管理esxi

安装vcenter管理esxi虚拟化操作系统 文章目录 安装vcenter管理esxi虚拟化操作系统1.安装vcenter2.vcenter的应用 1.安装vcenter esxi虚拟机具体安装步骤请参考上一篇文章&#xff0c;vcenter软件包需自己到网上下 2.vcenter的应用

电机学(笔记一)

磁极对数p&#xff1a; 直流电机的磁极对数是指电机定子的磁极对数&#xff0c;也等于电机电刷的对数。它与电机的转速和扭矩有直接关系。一般来说&#xff0c;极对数越多&#xff0c;电机转速越低&#xff0c;扭矩越大&#xff0c;适用于低速、高扭矩的场合&#xff1b;相反&…

Java-JVM 虚拟机原理调优实战

一、基础 栈帧&#xff08;Stack Frame&#xff09;栈空间的 基本元素&#xff0c;用于 方法的调用和方法的执行的数据结构 堆内存用来存放由new创建的对象和数组。在堆中分配的内存&#xff0c;由Java虚拟机的自动垃圾回收器来管理。在堆中产生了一个数组或对象后&#xff0c…

MATLAB的使用(一)

一&#xff0c;MATLAB的编程特点 a,语法高度简化&#xff1b; b,脚本式解释型语言&#xff1b; c,针对矩阵的高性能运算&#xff1b; d,丰富的函数工具箱支持&#xff1b; e,通过matlab本体构建跨平台&#xff1b; 二&#xff0c;MATLAB的界面 工具栏:提供快捷操作编辑器…

2024年腾讯云服务器租用价格表_1个月一年3年5年和1小时费用表

腾讯云服务器租用优惠价格表&#xff1a;轻量应用服务器2核2G3M价格61元一年、2核2G4M价格99元一年&#xff0c;540元三年、2核4G5M带宽165元一年&#xff0c;2核4G5M带宽756元三年、轻量4核8G12M服务器646元15个月&#xff0c;轻量4核16G12M服务器32元1个月、312元一年&#x…

RabbitMQ介绍及搭建

架构 RabbitMQ是实现了高级消息队列协议&#xff08;AMQP&#xff09;的开源消息代理软件&#xff0c;使用erlang语言编写&#xff0c;依赖Erlang环境运行。 Broker&#xff1a;运行消息队列服务进程的节点&#xff0c;包含Exchange、Queue&#xff1b; Producer&#xff1a;消…

Java进阶 Maven基础

资料格式 配置文件 com.itheima Java代码 Statement stat con.createStatement(); 示例 com.itheima 命令 mvn test - Maven简介 传统项目管理状态分析 Maven 是什么 Maven的本质是一个项目管理工具&#xff0c;将项目开发过程抽象成一个项目对象模型&#xff08;POM&…

HTTP 工作流程请求响应 - 面试常问

文章目录 HTTP 工作流程请求和响应格式HTTP请求格式请求行&#xff1a;请求头部字段&#xff1a;空行&#xff1a;消息正文&#xff08;请求正文&#xff09;&#xff1a; HTTP响应格式状态行&#xff1a;响应头部字段&#xff1a;空行&#xff1a; HTTP方法HTTP状态码常用HTTP…

yocto编译测试

源码下载 git clone -b gatesgarth git://git.yoctoproject.org/poky lkmaolkmao-virtual-machine:~/yocto$ git clone -b gatesgarth git://git.yoctoproject.org/poky Cloning into poky... remote: Enumerating objects: 640690, done. remote: Counting objects: 100% (13…