C++函数模板

2023-05-16


前言

        模板是一个通用框架,是C++泛型编程思想的主要体现。C++提供了函数模板类模板两种模板机制,本文介绍的是函数模板相关的知识。


一、函数模板的作用及语法

        作用:用一个虚拟的类型来代表函数的返回值类型和形参的类型,在调用函数时根据实际需求传不同类型的参数以达到函数通用的目的,增加代码的复用性。   

        语法:     

template <typename T> 
返回值 函数名(参数){}

        解释:
        template 声明创建模板
        typename 也可以用class,后面接的符号代表一种数据类型
        T 是一个通用的数据类型,名称自定义,通常为大写字母 

        示例: 交换两个数

void swapInt(int &a, int &b)    //两个整型交换
{
	int temp = a;
	a = b;
	b = temp;
}
void swapDouble(double &a, double &b)  //两个浮点型交换
{
	double temp = a;
	a = b;
	b = temp;
}
void Swaptest()
{
	int a = 10;
	int b = 20;
	swapInt(a, b);
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;

	double c = 30;
	double d = 40;
	swapDouble(c, d);
	cout << "c = " << c << endl;
	cout << "d = " << d << endl;
}	   
int main()
{
	Swaptest();
	return 0;
}

        上面这种方式每一种类型都要分别写一个对应的交换函数但是swapInt函数和swapDouble函数代码大部分都是一样的,只是函数名和参数类型有所不同,对于此我们可以使用函数模板的方式来提高代码复用率,减少代码量。具体代码如下所示:

//函数模板
template<typename T> //声明一个模板,告诉编译器后面代码中紧跟的T不要报错,T是一个通用数据类型
void MySwap(T &a, T &b)
{
	T temp = a;
	a = b;
	b = temp;
}
void Swaptest()
{
	int a = 10;
	int b = 20;
	
	//利用函数模板交换
	//调用函数模板有隐式类型推导和显式类型推导两种方式
	// 1、隐式类型推导
	MySwap(a, b);
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;

	double c = 30.0;
	double d = 40.0;
	
	// 2、显式类型推导
	MySwap<double>(c, d);
	cout << "c = " << c << endl;
	cout << "d = " << d << endl;
}	   
int main()
{
	Swaptest();
	return 0;
}

        函数模板调用方式有两种:显式类型推导隐式类型推导

需要注意的是:

        使用显式类型推导,参数和推导的类型必须一致。

        如果有普通函数和模板函数,在调用时可以用显式调用,省略类型的方式  MySwap<>(1,2)


 二、函数模板案例

案例描述:

  • 利用函数模板封装一个排序函数,可以对不同数据类型数组进行排序
  • 排序规则从大到小,排序算法为选择排序

案例: 

#include <iostream>
#include <string>

using namespace std;

template<class T>
void mySwap(T &a, T &b)    //交换函数 模板
{
	T temp = a;
	a = b;
	b = temp;
}
template<typename T>  
void mySort(T arr[], int len)    //排序算法
{
	for (int i = 0; i < len; i++)
	{
		int max = i; //认定最大值的下标
		for (int j = i; j < len; j++)
		{
			if (arr[max] < arr[j])
			{
				max = j; //更新最大值下标
			}
		}
		mySwap(arr[max], arr[i]);    //交换max和i元素
	}
}
template<typename T>
void printArray(T arr[], int len)    
{
	for (int i = 0; i < len; i++)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}
void Mytest()
{
	char charArr[] = "badcfe";    //测试char数组
	int num = sizeof(charArr) / sizeof(char);
	mySort(charArr, num);
	printArray(charArr, num);
}	   
int main()
{
	Mytest();
	return 0;
}

三、普通函数与函数模板的区别

普通函数与函数模板的区别:

  • 普通函数只可以有一种数据类型相匹配。函数模板有多种类型
  • 隐式推导优先使用普通函数,只有普通函数不匹配才使用函数模板
  • 函数模板只有在调用时,才会构建函数,而普通函数是在编译时
  • 普通函数调用时候可以发生自动类型转换,而函数模板不行 

 示例

int myAdd1(int a, int b)    //普通函数
{
	return a + b;
}

template<class T>
int myAdd2(T a, T b)    //函数模板
{
	return a + b;
}

void Mytest()
{
	int a = 10;
	int b = 20;
	char c = 'c';
	cout << myAdd1(a, c) << endl;

	//隐式类型推导
	cout << myAdd2(a, b) << endl;
	//cout << myAdd2(a, c) << endl;会报错,T的类型不一致

	//显示指定类型
	myAdd2<int>(a, c); //不会报错,会发生隐式类型转换
}
	   
int main()
{
	Mytest();
	return 0;
}


 四、普通函数与函数模板的调用规则

        调用规则如下:

  • 如果函数模板和普通函数都可以实现,优先调用普通函数

void myPrint(int a, int b)
{
	cout << "调用的普通函数" << endl;
}

template<typename T>
void myPrint(T a, T b)
{
	cout << "调用的是函数模板" << endl;
}
void Mytest()
{
	int a = 10;
	int b = 20;
	myPrint(a, b);

}	   
int main()
{
	Mytest();
	return 0;
}

        运行结果: 


  •  可以通过空模板参数列表来强制调用函数模板

void myPrint(int a, int b)
{
	cout << "调用的普通函数" << endl;
}

template<typename T>
void myPrint(T a, T b)
{
	cout << "调用的是函数模板" << endl;
}
void Mytest()
{
	int a = 10;
	int b = 20;
	myPrint<>(a, b);    //通过空模板参数列表来强制调用函数模板
}   
int main()
{
	Mytest();
	return 0;
}

         运行结果:


  • 函数模板也可以发生重载

template<typename T>
void myPrint(T a, T b)
{
	cout << "调用的是函数模板" << endl;
}

template<typename T>
void myPrint(T a, T b,T c)
{
	cout << "调用的是重载的函数模板" << endl;
}
void Mytest()
{
	int a = 10;
	int b = 20;
	myPrint(a, b, 100);

}  
int main()
{
	Mytest();
	return 0;
}

 运行结果:


  • 如果函数模板可以产生更好的匹配,优先调用函数模板

void myPrint(int a, int b)
{
	cout << "调用的普通函数" << endl;
}

template<typename T>
void myPrint(T a, T b)
{
	cout << "调用的是函数模板" << endl;
}
void Mytest()
{
	char c1 = 'a';
	char c2 = 'b';
	myPrint(c1, c2);
}   
int main()
{
	Mytest();
	return 0;
}

 运行结果:


  • 模板的重载与嵌套使用 

#include <iostream>

using namespace std;
template <class T>
T Max(T a,T b)
{
    return((a > b)?a : b);
}
template <class T>
T Max(T a,T b,T c)
{
    return Max(Max(a,b),c);    //嵌套
}
template <class T>
T Max(T a,T b,T c,T d)
{
    return Max(Max(a,b,c),d);    //嵌套
}

int main(int argc, char *argv[])
{
    cout << Max(8,11) << endl;
    cout << Max(4,8,9) << endl;
    cout << Max(4.2,2.8,5.9) << endl;

    cout << Max<float>(5.4,3.8,2.9) << endl;
    cout << Max<float>(5.4,3.8,2.9,8.1) << endl;
    return 0;
}

运行结果:


  • 函数模板的特化

    定义:为了解决函数模板的局限性,在定义函数模板时候,直接确定好T的类型。也就是特定的类型模板
    格式: template<class T> 返回值  函数名(类名& 对象){ }
    匹配:如果传入的是自定义类型,并且和特化版本匹配,会优先使用特化版本
    注意:如果特化后,类型确定才可以使用自定义类型中的成员变量和方法。

class Person
{
public:
	Person(string name, int age)
	{
		this->m_Age = age;
		this->m_Name = name;
	}
	int m_Age;
	string m_Name;
};

//对比两个数据是否相等
template<typename T>
bool myCompare(T &a, T &b)
{
	if (a == b)
	{
		return true;
	}
	else
	{
		return false;
	}
}
//利用具体化Person的版本实现代码,具体化优点调用
template<> bool myCompare(Person &p1, Person &p2)
{
	if (p1.m_Age == p2.m_Age && p1.m_Name == p2.m_Name)
	{
		return true;
	}
	else
	{
		return false;
	}
}
void Mytest()
{
	Person a("danny", 20);
	Person b("danny", 20);
	bool ret = myCompare(a, b);
	if (ret)
	{
		cout << "a == b" << endl;
	}
	else
	{
		cout << "a != b" << endl;
	}		
}   
int main()
{
	Mytest();
	return 0;
}


        如果不写具体化的版本,代码就会报错。原因是出现自定义类型的比较时,编译器无法识别。可以在Person类中重载==运算符的形式来解决问题,也可以利用具体化的模板解决自定义类型的通用化。


注:本文主要为在长沙华清远见 C++课程时整理的笔记及测试程序,部分内容参考B站黑马程序员C++课程。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++函数模板 的相关文章

随机推荐

  • 字符串的查找以及查重方法总结

    掌握要求 xff1a 1 字符串查找的朴素算法 2 字符串查找的KMP算法 3 哈希表 4 字典树 1 字符串的查找之朴素算法 朴素算法思想很简单 xff0c 就是将待查找字符串t在被查找的字符串s中一一对比 xff0c 如果遇到相同字符就
  • ROS18.04:安装gazebo,下载模型

    一 安装gazebo Ubuntu18 04安装Gazebo并与ROS连接 Jize 的博客 CSDN博客 ubuntu18安装gazebo 其中报错 xff1a 解决 xff1a sudo apt upgrade 二 下载模型 方法一 x
  • Github使用指南(持续更新中)

    一 简介 Github的网页端是www github com GitHub是一个面向开源及私有软件项目的托管平台 xff0c 因为只支持Git作为唯一的版本库格式进行托管 xff0c 故名GitHub GitHub于2008年4月10日正式
  • Linux安装离线版docker

    Linux安装离线版docker 1 docker 离线包下载地址 https download docker com linux static stable x86 64 2 创建 docker service xff0c 代码中 ins
  • Android Studio 超详细 安装SDK 教程

    一 首先安装Android SDK Tools 国内下载地址Android SDK Tools 百度云下载地址链接 xff1a https pan baidu com s 1RmXi8b lxksVS5hJuSLPIg 提取码 xff1a
  • 【Android Studio】win10安装教程 史上最详细

    一 安装Android Studio 1 首先下载Android Studio 官网下载 xff1a Androis Studio 官网 2 双击打开 点击Next 3 选择安装 xff0c 点击Next 4 选择安装路径 xff0c 点击
  • Android Studio 创建手机虚拟机教程

    小白 Android Studio创建手机虚拟机 版本 AndroidStudio 3 5 3 在APP开发中不一定每时每刻都有真机 xff0c 所以可以通过AndroidStudio 创建一个手机虚拟机 1 创建新的手机虚拟机 点击No
  • Android Studio 下载设备虚拟机镜像

    这里以下载手机虚拟机镜像为示例 需要用到的软件 Android SDK Tools 不知道如何安装Android SDK Tools 的请参考 安装Android SDK Tools SDK Manager exe打不开的请参考这里 SDK
  • 【嵌入式】交叉编译 移植 i2cTool4.1 史上最详细

    一 i2ctool 介绍 i2c tools工具是一个专门用来调试 i2c 的 并且是开源的 他可以 xff1a 1 检测有几组i2c总线在系统上 2 查看挂载在i2c上面设备寄存器的数值 3 可以读取挂载在i2c上面设备寄存器的数值 4
  • 【Ubuntu】 vim 安装与美化 史上最详细

    一 安装vim sudo apt get install vim 二 配置vim 针对单个用户配置 vim vimrc 针对全部用户配置 vim etc vim vimrc 三 配置选项 1 我的配置 34 设置编码 set fileenc
  • 【Ubuntu】Ubuntu16.04安装 搜狗输入法 史上最详细

    一 基本配置 开发环境 xff1a ubuntu16 04 二 准备工作 下载 搜狗输入法linux版 搜狗输入法官网Linux版 我这里是64位的 xff0c 所以选择64位 1 拷贝搜狗输入法到ubuntu cp media sf sh
  • 【嵌入式】---交叉编译 移植 ALSA1.2.2

    一 开发环境 开发环境 xff1a ubuntu16 04 开发平台 xff1a imx6q 交叉编译工具链 xff1a arm linux gnueabihf 二 需要的软件 1 alsa lib 下载地址 xff1a alsa lib
  • 【Android Studio 】经典常用开发设置 [最新最详细-持续更新]

    概述 一个好的配置能够帮助开发者完成更便捷 更快速的开发书山有路勤为径 xff0c 学海无涯苦作舟 我是秋知叶i 期望每一个阅读了我的文章的开发者都能够有所成长 一 当前 Android Studio 版本 Android Studio 经
  • 【MobaXterm】登录SSH服务器

    登录SSH远程服务器 一 配置用户信息1 打开Session2 打开SSH3 新建用户4 创建用户 二 配置登录SSH服务器信息三 再次登录服务器 一 配置用户信息 1 打开Session 2 打开SSH 3 新建用户 4 创建用户 输入账
  • 【adb】 win11 配置 adb环境 史上最详细

    官网下载 adb官网下载 下载下来的压缩包platform tools r33 0 3 windows zip 我们解压得到一个platform tools 二 配置adb环境 新建系统变量 点击浏览目录选择解压的文件夹 xff0c 然后点
  • 【Dev-c++】美化配置

    概述 一个好的配置能够帮助开发者完成更便捷 更快速的开发书山有路勤为径 xff0c 学海无涯苦作舟 我是秋知叶i 期望每一个阅读了我的文章的开发者都能够有所成长 一 设置语法格式 点击工具 编辑器选项 选择 语法 点击预设这里选择 Plas
  • 嵌入式Linux调试器GDB的使用

    调试一直是程序开发的重中之重 xff0c 使用GDB调试可以帮助我们快速找到程序中的错误 注意 xff1a 在进行GDB调试之前 xff0c 程序在gcc编译时要加上 g 选项 1 进入GDB xff1a gdb 可执行文件名 2 查看GD
  • 华清数据结构项目实训——学生信息管理系统

    模块划分及主要文件 1 主程序模块 主要功能 xff1a 程序的入口 主要文件 xff1a main c 2 菜单模块 主要功能 xff1a 完成菜单的显示以及登录和调用相应功能函数 主要文件 xff1a menu h menu c 3 学
  • sqlite3的安装以及增删改查排序功能的实现

    目录 一 安装sqlite3 1 安装sqlite3数据库 xff1a 2 安装编译依赖库 3 安装可视化界面 4 验证数据库是否安装成功 二 常用数据库指令及SQL数据类型 1 常用数据库指令 2 常用SQL数据类型 三 数据库操作 1
  • C++函数模板

    前言 模板是一个通用框架 xff0c 是C 43 43 泛型编程 思想的主要体现 C 43 43 提供了函数模板 和类模板 两种模板机制 xff0c 本文介绍的是函数模板相关的知识 一 函数模板的作用及语法 作用 xff1a 用一个虚拟的类