【033】C++泛型编程(模板)之函数模板详解

引言


💡 作者简介:专注于C/C++高性能程序设计和开发,理论与代码实践结合,让世界没有难学的技术。包括C/C++、Linux、MySQL、Redis、TCP/IP、协程、网络编程等。
👉
🎖️ CSDN实力新星,社区专家博主
👉
🔔 专栏介绍:从零到c++精通的学习之路。内容包括C++基础编程、中级编程、高级编程;掌握各个知识点。
👉
🔔 专栏地址:C++从零开始到精通
👉
🔔 博客主页:https://blog.csdn.net/Long_xu


🔔 上一篇:【032】C++高级开发之多态技术详解(虚函数最全讲解)

一、模板的概述

C++提供了函数模板(function template)。函数模板实际上是建立一个通用函数,其函数类型和形参不具体指定,用一个虚拟的类型来代表,这个通过函数就成为函数模板。凡是函数体相同的函数都可以用函数模板替代,不必定义多个函数,只需要在模板中定义一次即可。在调用函数时系统会根据实参的类型来期待模板中的虚拟类型,从而实现不同函数的功能。

C++提供两种模板机制:函数模板和类模板。类属于类型参数化,又称参数模板。

小结一下:

  • C++面向对象编程思想:封装、继承、多态。
  • C++泛型编程思想:模板。

将功能相同、类型不同的函数(类)的类型抽象成虚拟的类型,当调用函数(类实例化对象)的时候,编译器自动将虚拟的类型具体化,这就是函数模板(类模板)。

二、函数模板

C++ 函数模板是一种用于创建通用函数的机制,可以让程序员编写一次函数,然后让它适用于多种类型,在实际编程中非常实用。

函数模板的定义需要包括函数模板的声明和函数模板的定义两部分。函数模板的声明包括函数模板的名称和函数模板的参数列表,函数模板的定义则包括函数模板的名称、函数模板的参数列表以及函数模板的函数体。需要注意的是,函数模板的参数可以是多个,用逗号隔开。

以下是一个简单的函数模板定义:

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

其中,template 关键字用于声明函数模板,typename 则是用于指定函数模板参数的类型,T 是函数模板参数的名称,可以根据需要进行替换。函数模板的实现和普通函数类似,只是函数模板的参数类型可以是任意类型。需要注意的是,函数模板的定义通常需要放在头文件中,以便在使用时能够正确地进行编译和链接。

在实际使用函数模板时,需要通过指定函数模板参数的实际类型来实例化函数模板。函数模板的实例化可以通过显式实例化、隐式实例化和模板特化三种方式来实现。其中,显式实例化是指通过在代码中显式地指定函数模板参数类型来实例化函数模板,隐式实例化是指通过函数调用时传递的实参类型来自动确定函数模板参数类型,模板特化则是指为特定的函数模板参数类型提供特定的实现方式。

函数模板可以使用类型别名、模板参数类型的默认值、模板特化等特性来增强其灵活性和可读性。

2.1、函数模板的定义方式

C++ 函数模板是一种用于创建通用函数的机制,可以让程序员编写一次函数,然后让它适用于多种类型,在实际编程中非常实用。

函数模板的定义方式包括两部分:函数模板的声明和函数模板的定义。函数模板的声明包含函数模板的名称以及函数模板的参数列表,函数模板的定义则包括函数模板的名称、函数模板的参数列表以及函数模板的函数体。

函数模板的声明方式如下:

template <typename T>
返回类型 函数名称(参数列表);

其中,template 关键字用于声明函数模板,typename 则是用于指定函数模板参数的类型,T 是函数模板参数的名称,可以根据需要进行替换。需要注意的是,函数模板的参数可以是多个,用逗号隔开。

函数模板的定义方式如下:

template <typename T>
返回类型 函数名称(参数列表) {
    // 函数体
}

其中,template 关键字和参数列表与函数模板的声明一致,函数体则是实现函数功能的代码。需要注意的是,函数模板的定义通常需要放在头文件中,以便在使用时能够正确地进行编译和链接。

在实际使用函数模板时,需要通过指定函数模板参数的实际类型来实例化函数模板,例如:

int a = 1, b = 2;
double x = 1.5, y = 2.5;
std::cout << max(a, b) << std::endl;  // 实例化 int 版本的 max 函数
std::cout << max(x, y) << std::endl;  // 实例化 double 版本的 max 函数

在这里,max 函数是一个函数模板,它可以适用于多种类型。通过传递不同类型的参数,可以实例化不同版本的 max 函数,从而实现通用的功能。

示例:

#include <iostream>
using namespace std;

template <typename T>
void swapAll(T &a, T &b)
{
	T tmp = a;
	a = b;
	b = tmp;
}


int main()
{
	int a = 100, b = 200;
	// 函数调用时,根据实参的类型,自动推导T的类型
	swapAll(a, b);
	cout << a << " " << b << endl;

	char c = 'c';
	char d = 'd';
	swapAll(c, d);
	cout << c << " " << d << endl;

	return 0;
}

输出:

200 100
d c

函数模板会编译两次:

  • 第一次,对函数模板本身编译。
  • 第二次,函数调用处,将T的类型具体化。

函数模板目标:模板是为了实现泛型编程,可以减轻编程的工作量,增强函数的重用性。

C++的标准算法中基本都是使用模板完成的,包括容器、算法。

2.2、函数模板的注意点

(1)当函数模板和普通函数都识别时,会优先使用普通函数。示例:

#include <iostream>
using namespace std;

template <typename T>
void swapAll(T &a, T &b)
{
	T tmp = a;
	a = b;
	b = tmp;
	cout << "函数模板" << endl;
}

void swapAll(int &a, int &b)
{
	int tmp = a;
	a = b;
	b = tmp;
	cout << "普通函数" << endl;
}

int main()
{
	int a = 100, b = 200;
	swapAll(a, b);// 调用普通函数
	cout << a << " " << b << endl;

	char c = 'c';
	char d = 'd';
	swapAll(c, d);// 调用函数模板
	cout << c << " " << d << endl;

	return 0;
}

输出:

普通函数
200 100
函数模板
d c

(2)函数模板和普通函数都识别时,强制使用函数模板需要使用<>指定。示例:

#include <iostream>
using namespace std;

template <typename T>
void swapAll(T &a, T &b)
{
	T tmp = a;
	a = b;
	b = tmp;
	cout << "函数模板" << endl;
}

void swapAll(int &a, int &b)
{
	int tmp = a;
	a = b;
	b = tmp;
	cout << "普通函数" << endl;
}

int main()
{
	int a = 100, b = 200;
	// 强制使用函数模板
	swapAll<>(a, b);// 调用函数模板,自动推导类型
	cout << a << " " << b << endl;
	swapAll<int>(a, b);// 调用函数模板,指定T类型是int
	cout << a << " " << b << endl;

	char c = 'c';
	char d = 'd';
	swapAll(c, d);// 调用函数模板
	cout << c << " " << d << endl;

	return 0;
}

输出:

函数模板
200 100
函数模板
100 200
函数模板
d c

(3)函数模板自动类型推导时,不能对函数的参数进行自动类型转换。示例:

#include <iostream>
using namespace std;

template <typename T>
void swapAll(T &a, T &b)
{
	T tmp = a;
	a = b;
	b = tmp;
	cout << "函数模板" << endl;
}

void swapAll(int &a, int &b)
{
	int tmp = a;
	a = b;
	b = tmp;
	cout << "普通函数" << endl;
}

int main()
{
	int a = 100, b = 200;
	char c = 'c';
	char d = 'd';
	// 强制使用函数模板
	swapAll(a, b);// 调用普通函数
	cout << a << " " << b << endl;
	//swapAll(a, c);// error,函数不存在,没有int char参数列表的函数

	//swapAll<int>(a, c);//指定T类型是int, error,函数不存在,没有int char参数列表的函数
	

	
	swapAll(c, d);// 调用函数模板
	cout << c << " " << d << endl;

	return 0;
}

函数模板的类型是推导出来的,如果类型不匹配,就无法推导出来,函数调用就会失败。

2.3、函数模板的重载

函数模板也是可以重载的,例如:

template <typename T>
void MyPrint(T a)
{
	cout<<a<<endl;
}

template <typename T>
void MyPrint(T a,T b)
{
	cout<<a<<endl;
	cout<<b<<endl;
}

2.4、函数模板的局限性

当函数模板推导出T为数组或其他自定义数据类型,可能导致运算符不识别。

解决方案一:运算符重载,推荐这种解决方案。

#include <iostream>
using namespace std;

class Data {
	friend ostream& operator<<(ostream &out, Data ob);
private:
	int data;
public:
	Data()
	{
		cout << "Data 构造函数" << endl;
	}
	Data(int data)
	{
		this->data = data;
		cout << "Data 有参构造函数" << endl;
	}
};

template <typename T>
void MyPrint(T a)
{
	cout << a << endl;
	cout << "函数模板" << endl;
}

//重载运算符
ostream& operator<<(ostream &out,Data ob)
{
	out << ob.data;
	return out;
}

int main()
{
	char c = 'c';

	MyPrint(c);// 调用函数模板

	Data obj(100);
	MyPrint(obj);
	

	return 0;
}

解决方案二:具体化函数模板。

#include <iostream>
using namespace std;

// 注意,这个函数模板的定义一定要在class之前
template <typename T>
void MyPrint(T a)
{
	cout << a << endl;
	cout << "函数模板" << endl;
}

class Data {
	friend void MyPrint<Data>(Data a);
private:
	int data;
public:
	Data()
	{
		cout << "Data 构造函数" << endl;
	}
	Data(int data)
	{
		this->data = data;
		cout << "Data 有参构造函数" << endl;
	}
};



//函数模板具体化
template <>
void MyPrint<Data>(Data a)
{
	cout << a.data << endl;
	cout << "函数模板具体化" << endl;
}

int main()
{
	char c = 'c';

	MyPrint(c);// 调用函数模板

	Data obj(100);
	MyPrint(obj);
	

	return 0;
}

输出:

c
函数模板
Data 有参构造函数
100
函数模板具体化

总结

C++ 函数模板是一种用于创建通用函数的机制,可以让程序员编写一次函数,然后让它适用于多种类型,在实际编程中非常实用。

函数模板的定义需要包括函数模板的声明和函数模板的定义两部分。函数模板的声明包括函数模板的名称和函数模板的参数列表,函数模板的定义则包括函数模板的名称、函数模板的参数列表以及函数模板的函数体。需要注意的是,函数模板的参数可以是多个,用逗号隔开。

在实际使用函数模板时,需要通过指定函数模板参数的实际类型来实例化函数模板。函数模板的实例化可以通过显式实例化、隐式实例化和模板特化三种方式来实现。其中,显式实例化是指通过在代码中显式地指定函数模板参数类型来实例化函数模板,隐式实例化是指通过函数调用时传递的实参类型来自动确定函数模板参数类型,模板特化则是指为特定的函数模板参数类型提供特定的实现方式。

需要注意的是,函数模板的定义通常需要放在头文件中,以便在使用时能够正确地进行编译和链接。此外,函数模板可以使用类型别名、模板参数类型的默认值、模板特化等特性来增强其灵活性和可读性。

在这里插入图片描述

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lion Long

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值