您现在的位置是:首页 > 正文

有关《函数模板》的那些小知识-.-

2024-01-30 20:53:16阅读 0

本文介绍函数模板的概念、用途以及如何创建函数模板和函数模板的使用方法......

函数模板定义的一般形式如下:

template<类型形式参数表>

返回类型 函数名(形式参数表)

{

... //函数体

}

之前我们知道的交换两个变量的方法有宏定义、函数,这两种方式都能实现两个变量的交换,但是各有各的优缺点

       宏定义: 

 -优点:

            代码复用,适合所有的类型

- 缺点:

            缺少类型检查,宏在预处理阶段就被替换掉,编译器并不知道宏的存在

       函数:

- 优点:

        真正的函数调用,编译器对类型进行检查

- 缺点:

        类型不同需要重复定义函数,代码无法复用

上边两种方式都各有利弊,但是在C++中,存在泛型编程的概念:即不考虑具体数据类型的编程方式(如下)

 对于函数体相同且函数的参数个数相同而参数类型不同的一系列函数而言,都是可以用函数模板来代替的,只需要定义一次函数模板即可。

         函数模板通过templatetypename(class代替 效果一致)两个关键字来定义,如下

上边就定义了一个变量交换的函数模板,在使用函数模板时有两种方式
    - 自动类型推到调用 Swap(a, b)
    - 具体类型显示调用 Swap<int>(a, b)

template<typename t>
t max(t a, t b)
{
	return a > b ? a : b;
}
int main()
{
	int a = 0;
		//模板参数列表中只有一种数据类型,传入的实参也只有⼀种数据类型
		
		//可以直接由函数模板生成模板函数
		//隐式实例化
	a = max(1, 2);

		//模板参数列表中只有⼀种数据类型,但是此时传入的实参有两种数据类型
		
		//在调用时需要强制的类型转换,保证实参的类型个数与模板参数列表的类型个数⼀致
		
    //由函数模板生成模板函数时编译器不能做到自动的隐式转换,需要显示的强制转换
	a = max(1, (int)2.0);
	a = max((double)1, 2.0);

	//显式转换也可以以尖括号形式的语法进行转换
    //显式实例化
	a = max<int>(1, 2.0);
	a = max<double>(1, 2.0);

	return 0;
}

需要注意的是:函数模板不支持隐式类型转换

还有一招:显式具体化

函数模板的显式具体化是对函数模板的重新定义,具体格式如下所示:

 template< > 函数返回值类型 函数名<实例化类型>(参数列表)
{
    //函数体重新定义
}

 显式实例化只需要显式声明模板参数的类型而不需要重新定义函数模板的实现,而显式具体化需要重新定义函数模板的实现

//定义交换两个数据的函数模板,示例如下
template<typename T>
void swap(T& t1, T& t2)
{
	T temp = t1;
	t1 = t2;
	t2 = temp;
}
//但现在有如下结构体定义
struct Student
{
	int id;
	char name[20];
	float score;
};
//现在我们只想交换两个学生的id,那么我们就可以用显式具体化解决这个问题
template<> void swap<Student>(Student& s1, Student& s2)
{
	int temp = s1.id;
	s1.id = s2.id;
	s2.id = temp;
}

如果函数有多个原型,则编译器在选择函数调用时,非模板函数优先于模板函数,显式具体化模板优先于函数模板,例如下面三种定义:

void swap(int&, int&);                         //直接定义
template<typename T>void swap(T& t1, T& t2);   //模板定义
template<> void swap<int>(int&, int&);         //显式具体化

如何理解呢?

                int a, int b存在swap(a, b)调用,会优先调用直接定义的函数;如果没有,则调用显式具体化 ;两者都不存在,这时候才去调用模板函数。

函数模板也是可以重载的,和函数重载差不多,这里就不细说了。 

使用函数模板时需要注意的问题: 

1) < > 中的每一个类型参数在函数模板参数列表中必须至少使用一次。

例如,下面的函数模板声明是不正确的。

template<typename T1, typename T2>
void func(T1 t)
{
}

函数模板声明了两个参数 T1 与 T2,但在使用时只使用了 T1,没有使用 T2。

2) 全局作用域中声明的与模板参数同名的对象、函数或类型,在函数模板中将被隐藏。

例如:

int num;
template<typename T>
void func(T t)
{
	T num;
	cout << num << endl;  //输出的是局部变量num,全局int类型的num被屏蔽
}

在函数体内访问的 num 是 T 类型的变量 num,而不是全局 int 类型的变量 num。(局部优先)

 3) 函数模板中声明的对象或类型不能与模板参数同名。

例如:

template<typename T>
void func(T t)
{
	typedef float T;  //错误,定义的类型与模板参数名相同
}

4) 模板参数名在同一模板参数列表中只能使用一次,但可在多个函数模板声明或定义之间重复使用。

例如:

template<typename T, typename T>  //错误,在同一个模板中重复定义模板参数
void func1(T t1, T t2) 
{}
template<typename T>
void func2(T t1) 
{}
template<typename T>   //在不同函数模板中可重复使用相同的模板参数名
void func3(T t1) 
{}

5) 模板的定义和多处声明所使用的模板参数名不是必须相同。

例如:

//模板的前向声明
template<typename T>
void func1(T t1, T t2);
//模板的定义
template<typename U>
void func1(U t1, U t2)
{
}

6) 如果函数模板有多个模板参数,则每个模板参数前都必须使用关键字 class 或 typename 修饰。

例如:

template<typename T, typename U>  //两个关键字可以混用
void func(T t, U u)
{}
template<typename T, U>   //错误,每一个模板参数前都必须有关键字修饰
void func(T t, U u) 
{}

这次的文章也是到这里就结束了。

如果觉得有用的话,点个赞支持一下吧!

谢谢各位大佬

网站文章

  • 辗转相除法求最大公约数

    辗转相除法求最大公约数

    由此可见,a和b都是d的倍数,所以d是a和b的最大公因数(最大是因为我们在第3步时就停止了分割线段,此时恰好整除,所以是最大)我们以及对求最大公因数有了形象上的了解,那么下面要做的就是将其写成代码。最...

    2024-01-30 20:52:47
  • Spring生命周期讲的比较好的文章

    https://www.jianshu.com/p/1dec08d290c1我画的流程图,参照:https://www.processon.com/view/link/5f915d30e401fd06fd9c0b96

    2024-01-30 20:52:35
  • html关系选择符,CSS关系选择器和属性选择器

    html关系选择符,CSS关系选择器和属性选择器

    1.1. 关系选择器关系选择器是通过元素之间的“位置关系的特征”来确定所选元素。1.1.1. 子代选择器:S1>S2语法:选择器1>选择器2匹配S1中的下一级S2。下一级就是“子级”,或子代。其中S1,S2都可以是独立使用的选择器(比如id选择器,class选择器,标签选择器等)。1.1.2. 后代选择器:S1 S2语法:选择器1 选择器2【派生选择器】匹配S1内部的所有后代S2。...

    2024-01-30 20:52:05
  • 本地MySQL数据库其他电脑局域网访问

    本地MySQL数据库其他电脑局域网访问

    一般情况下是访问不了的 ,需要改两个地方首先查看局域网本地ipipconfig第一步首先本地用 Navicat 本地连接 打开数据库 (MySQL)/user表把user字段为root的 Host字段改成%第二步打开Windows防火墙 点击高级设置步骤阅读点击进去 点入站规则在点击新建规则选择端口 进行下一步填入330...

    2024-01-30 20:52:00
  • Intel8086处理器使用NASM汇编语言实现操作系统04-实模式-屏幕显示不定长度的字符串(cmp/je)

    怀念二抱三抱

    2024-01-30 20:51:52
  • 【自学笔记】web前端 - HTML - DAY01(编辑器+基础标签)

    【自学笔记】web前端 - HTML - DAY01(编辑器+基础标签)

    这里是Jane的自学笔记之Web前端系列~ (希望周更ᕦ(・ㅂ・)ᕤ)如果内容有问题,欢迎大家私信留言、批评指出,谢谢~主要是记录和分享,嘻嘻(〃‘▽’〃)文章目录编辑器HBuilder记事本Node...

    2024-01-30 20:51:25
  • Android之Activity的4种加载模式

    一个Task可以理解成一个Activtiy栈,可以装载一个或者多个Activity,回退和打开的顺序逻辑和基本的数据结构栈是一致的。下面的这段话非常关键:那就是,一个Application如果有N个Activity,这些Activity分布在M个Task中,那么Application的回退栈会遵循这样的原则,首先从当前Acitivty在的Task回退,直到这个Task中再无记录;那么会寻找下一个T

    2024-01-30 20:51:16
  • 分享一个随手写的简单css效果

    分享一个随手写的简单css效果

    随手制作的简单css效果前言效果图代码HTMLCSS介绍推荐Uigradients中国传统配色网站Colordrop前言 刚刚随手写了一个网页,添加了一点css的效果,虽然没有很惊艳,但个人认为有点意...

    2024-01-30 20:51:07
  • ajax首页首页显示数据库,ajax显示mysql数据库

    ajax首页首页显示数据库,ajax显示mysql数据库

    ajax显示mysql数据库 内容精选换一换GaussDB(for MySQL)支持的数据库版本,如表1所示。GaussDB(for MySQL)支持内核小版本升级,内核小版本的升级涉及性能提升、新功...

    2024-01-30 20:51:00
  • Qt中的进程与线程

    一、进程内容: 如何在Qt应用程序中启动一个进程; 进程间通讯方法; 使用进程的原因: (1)不希望将一个不太相关的功能集成到程序中 (2)或者是因为该功能与当前设计的应用程序联系不大 (3)或者是因为该功能已经可以使用现成的程序很好实现,可以使用进程调用外部的程序来实现该功能。 I、运行一个进程——QProcess类(继承自QIOdevice) 启动一个进程:调用start函数 ...

    2024-01-30 20:50:31