C++浅谈模板

1、 泛型编程

void Swap(int& left, int& right)
{
	int temp = left;
	left = right;
	right = temp;
}
void Swap(double& left, double& right)
{
	double temp = left;
	left = right;
	right = temp;
}
void Swap(char& left, char& right)
{
	char temp = left;
	left = right;
	right = temp;
}

这是几个交换函数的重载,每个函数的逻辑相似,只有传参的类型不同。写一份几乎相同的函数,冗余而且很难维护

能不能写出一种通用函数,能够接收不同类型的参数呢?

  • 在C++中用泛型编程解决了这个问题
  • 泛型编程用于编写与类型无关的通用代码,同时保证了类型安全,实现一份代码适配多种类型
  • C++主要通过模板实现泛型编程,包括函数模板和类模板

2、 函数模板

函数模板是一种“通用函数”,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本

语法://声明类型 参数T,typename可以换成class(不可以换成struct)

  //template <typename T1, typename T2>
 //返回值类型 函数名(参数列表){函数模板实现}

下面是用函数模板写的交换函数

#include<iostream>
using namespace std;

/T是类型参数,表示任意类型
template<typename T>
void Swap(T& a, T& b)
{
	T tmp = a;
	a = b;
	b = tmp;
}

int main()
{
	int x = 1, y = 2;
	/自动推导出T=int,生成Swap<int>示例
	Swap(x, y);
	cout << x << " " << y << endl;

	double a = 1.1, b = 2.2;
	/自动推导出T=double,生成Swap<double>示例
	Swap(a, b);
	cout << a << " " << b << endl;

	string s1 = "haha", s2 = "hehe";
	/自动推导出T=string,生成Swap<string>示例
	Swap(s1, s2);
	cout << s1 << " " << s2 << endl;

	return 0;
}

函数模板原理

函数模板是一种“蓝图”,定义时不指定具体类型,用类型参数(typename T)代替。编译器通过传入的实参自动推导类型,并实现模板实例化。

简单来说,模板时编译器自动生成了很多份函数重载

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/df58364f09484f8485127657f586779b.png

3、 函数模板实例化

1)隐式实例化, 编译器根据函数调用的实参自动推导类型参数,并生成对应实例
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/59b460473f394e79a868186be2270d60.png

2) 显式实例化, 在函数名后的<>中指定模板参数的实际类型
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/09eb668fcd5a43849ae8d938cf37d13d.png

4、 模板函数的匹配原则

  • 如果函数模板与普通函数重载同时存在,编译器遵循优先级:精确匹配的普通函数>显式指定类型的模板函数>模板函数的隐式实例化>隐式类型转后的普通函数(模板函数不允许自动类型转换,但普通函数可以进行自动类型转换)
  • 强调:模板函数不允许自动类型转换,必须严格匹配推导的类型(或显式指定类型)
// 普通函数
void f(int a) {
    cout << "普通函数\n";
}

// 函数模板
template<class T>
void f(T a) {
    cout << "模板函数\n";
}

int main() {
    f(10);       // 1. 精确匹配普通函数 → 调用普通函数
    f<int>(10);  // 2. 显式指定模板 → 调用模板
    f('a');      // 3. 普通函数需要char转int,模板精确匹配 → 调用模板
    f(3.14);     // 4. 普通函数不匹配,模板匹配 → 调用模板
}
//普通函数(支持自动类型转换)
// 普通函数
void func(int a)
{
    cout << "普通 int 函数" << endl;
}

int main()
{
    func(10);    // 精确匹配
    func('a');   // char 自动转 int (允许)
    func(3.14);  // double 自动转 int (允许)
}

//模板函数(不允许自动类型转换)
template<class T>
void add(T a, T b)
{
}

int main()
{
    add(10, 20);    //  T=int
    add(1.5, 2.5);  //  T=double

    add(10, 2.5);   //  报错!
    // 编译器:
    // 第一个参数是 int → T=int
    // 第二个参数是 double → T=double
    // 类型冲突!
    // 模板绝不帮你把 int 转 double
}

5、 类模板

语法
在这里插入图片描述

类模板的实例化

使用类模板时,需显式指定类型参数(编译器无法像函数模板那样自动推导类的类型参数),将实例化的类型放入<>,类模板名字不是真正的类,实例化的结果才是真正的类

#include<iostream>
using namespace std;

/类模板:通用栈
template<class T>
class Stack
{
	public:
		Stack(int capacity = 4)
			:_data(new T[capacity])
			,_top(-1)
			,_capacity(capacity)
		{}
		~Stack()
		{
			delete[]_data;
		}
		void push(const T& val)
		{
			...
		}
private:
	T* _data;
	int _top;
	int _capacity;
};

int main()
{
	/实例化int类型的栈
   /Stack 是类名,Stack<int>才是类型
   
   //Stack s;   //错误!类模板不能自动推导
	Stack<int> s;

	/实例化string类型的栈
	
	Stack<string> t;
}



注意区分类函数和类模板

为什么类模板不能自动推导?

因为类是在定义对象时确定类型,不像函数有参数可以用来推导。
// 没有参数给编译器看,编译器怎么知道 T 是什么?MyStack s;

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐