C++ 函数模板
一、为什么要有函数模板?
先看痛点:你要写两个交换函数,int 版、double 版:
cpp
// int 交换
void swapInt(int &a, int &b)
{
int t = a; a = b; b = t;
}
// double 交换
void swapDouble(double &a, double &b)
{
double t = a; a = b; b = t;
}
逻辑完全一样 ,只是类型不一样,重复写代码太冗余。
函数模板作用 :写一份通用逻辑 ,不指定具体类型,编译器根据你传的类型,自动生成对应版本的函数。
一句话:一份模板,适配所有类型。
二、函数模板 标准语法
1. 模板头写法
两种写法都可以:
cpp
template <typename T> // 写法1:推荐
template <class T> // 写法2:老式写法,效果一样
-
template:关键字,声明这是模板 -
<>里面是模板参数 -
T是类型参数,代表任意一种通用类型(自己起名,T/T1/T2 都行)
2. 完整函数模板格式
cpp
template <typename T>
函数返回值 函数名(形参列表)
{
函数逻辑,里面用 T 当类型用
}
三、写第一个函数模板:通用交换函数
cpp
#include <iostream>
using namespace std;
// 定义函数模板
template <typename T>
void mySwap(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
int main()
{
int x = 10, y = 20;
mySwap(x, y); // 自动推导 T = int
cout << x << " " << y << endl;
double m = 1.1, n = 2.2;
mySwap(m, n); // 自动推导 T = double
cout << m << " " << n << endl;
return 0;
}
核心关键点
-
你只写了一份代码
-
编译器在编译时,自动实例化 出
int版、double版函数 -
不用自己重复写重载函数
四、函数模板的两种调用方式
1. 自动类型推导(常用)
编译器根据实参,自己猜出 T 是什么类型
cpp
mySwap(x, y); // 自动推导出 T=int
要求:形参类型必须完全一致,不能一个 int 一个 double。
2. 显式指定类型
手动告诉编译器 T 是什么类型
cpp
mySwap<int>(x, y);
mySwap<double>(m, n);
适用场景:
-
无法自动推导时
-
强制指定类型转换
五、模板支持 多个类型参数
可以定义多个通用类型 T1、T2
cpp
template <typename T1, typename T2>
void printTwo(T1 a, T2 b)
{
cout << a << " , " << b << endl;
}
// 使用
printTwo(100, 3.14);
printTwo("hello", 666);
六、普通函数 和 函数模板 同时存在(优先级规则)
-
如果普通函数能匹配上 :优先调用普通函数
-
如果普通函数匹配不上,编译器会实例化模板调用
-
可以用
空模板参数<>强制调用模板函数
示例:
cpp
// 普通函数
void mySwap(int &a, int &b)
{
cout << "调用普通函数" << endl;
}
// 函数模板
template <typename T>
void mySwap(T &a, T &b)
{
cout << "调用函数模板" << endl;
}
int main()
{
int a=1,b=2;
mySwap(a,b); // 优先普通函数
mySwap<>(a,b); // 强制调用模板
double c=1.1,d=2.2;
mySwap(c,d); // 只能匹配模板,调用模板
return 0;
}
考试面试必背规则:
普通函数优先匹配;模板做备胎;
<>强制走模板。
七、函数模板 具体化 / 特化(重点)
场景:大部分类型用通用模板,唯独某一个类型要单独特殊处理 ,就用模板特化。
语法格式:
cpp
// 通用模板
template <typename T>
void show(T a)
{
cout << "通用版本:" << a << endl;
}
// 针对 string 类型 特化版本
template <>
void show<string>(string a)
{
cout << "string 特化版本:" << a << endl;
}
调用规则:
匹配到特化版本 → 优先用特化;否则用通用模板。
八、函数模板 底层原理(必须理解)
-
模板本身不生成函数,只是一张 "图纸"
-
编译阶段,根据实参类型 ,按照图纸生成对应类型的函数 ,这个过程叫 模板实例化
-
用多少种类型,就生成多少个重载版本
-
只在第一次使用该类型时实例化,后续复用
九、函数模板 使用限制(易错点 必记)
- 自动推导时,实参类型必须严格一致
cpp
template<typename T>
void func(T a, T b);
func(10, 3.14); // 报错!一个int 一个double,推导冲突
解决:显式指定 func<int>(10,3.14)
-
模板里不能出现无法通用的操作
比如模板里写
T++,如果 T 是字符串、自定义类且没重载 ++,直接报错。 -
模板声明和实现不能拆分到 .h 和 .cpp
因为编译时要看到完整模板代码,否则无法实例化;
工程里模板一般全部写在头文件里。
-
不能用局部类型、匿名类型做模板参数
十、一句话浓缩 必背考点
-
函数模板用
template<class T>或template<typename T>定义; -
作用:一份代码适配多种数据类型,减少冗余;
-
调用方式:自动类型推导、显式指定类型;
-
普通函数和模板共存:普通函数优先,<> 强制走模板;
-
特化模板:给特定类型单独定制逻辑;
-
原理:模板是图纸,使用时才实例化生成具体函数;
-
模板代码建议全写在头文件,不要分离声明实现。