函数模板
基本概念
重载与模板的核心区别
| 适用场景 | 技术选择 | 核心特征 |
|---|---|---|
| 同名函数,参数列表不同 + 算法不同 | 函数重载 | 逻辑差异化,需单独实现每个函数 |
| 同名函数,仅参数类型不同 + 算法一致 | 函数模板 | 逻辑统一,代码抽象化,避免冗余 |
函数模板的本质
- 函数模板是一个设计蓝图,而非具体函数,不占用内存
- 仅当发生具体调用并匹配成功后,编译器才会生成对应的函数实例(实例化过程)
典型场景示例
需求:设计“求两个数据最大值”的函数
- 若用重载:需为int、float、double等每种类型写单独函数,算法完全重复,代码臃肿
- 若用模板:仅需1个模板蓝图,编译器根据调用时的参数类型自动生成对应函数实例
函数模板语法
函数模板的定义
核心关键字
- template:声明后续为模板定义
- typename:指定类型参数(旧版可用class替代,功能完全一致)
语法格式
// 类型参数列表(可多个,用逗号分隔)
template <typename T1, typename T2> // T1、T2为类型参数(占位符)
void someFunc(T1 a, T2 b) // 用类型参数定义数据参数
{
// 算法逻辑(与类型无关,保持统一)
}
调用方式
函数模板有两个参数列表,调用时灵活选择:
- 显式指定类型参数:尖括号
<>传递类型,圆括号()传递数据someFunc<const char *, int>("abcd", 100); // 明确指定T1=const char*,T2=int - 自动推导类型参数:省略尖括号,编译器根据实参类型推导
someFunc("abcd", 100); // 编译器自动推导T1=const char*,T2=int
函数模板的重载
重载起因
一个模板仅能覆盖“参数个数相同、算法一致”的场景,若需满足:
- 参数个数不同
- 算法逻辑不同
则需重载模板(得到另一堆同名函数集合)
语法示例
// 模板1:单参数函数集合(任意类型T)
template <typename T>
void f(T a)
{
cout << "一堆接受一个任意参数的函数" << endl;
}
// 模板2:双参数函数集合(任意类型T1、T2)—— 重载模板1
template <typename T1, typename T2>
void f(T1 a, T2 b)
{
cout << "另一堆接受两个参数的函数" << endl;
}
说明
- 函数模板重载规则与普通函数一致:同名不同参数列表(个数/类型/顺序)
- 重载后,编译器根据调用时的实参个数/类型匹配对应的模板
函数模板的特化
特化定义
当某个特定类型组合无法套用模板的统一算法时,将该类型的函数版本单独定义(特殊化实现)
特化起因(典型场景)
模板解决“类型不同但算法一致”的问题,如“喂养动物”:
// 通用模板:适用于大部分动物+饲料组合
template <typename T1, typename T2>
void feed(T1 animal, T2 food)
{
// 通用算法:倒饲料到盆 → 叫动物来吃
}
但“喂养鲸鱼”的算法不同(无法套用通用逻辑),需特化模板
语法要点
- 保留
template关键字,尖括号<>不可省略(即使无剩余类型参数) - 将特化的类型从“类型参数列表”移除,在“数据参数列表”中写具体类型
- 特化版本的函数名与原模板一致
特化示例
// 1. 部分特化(仅特化部分类型参数:T1=Whale,T2仍为通用类型)
template <typename T2>
void feed(Whale w, T2 food)
{
// 鲸鱼专属算法:将食物投入水池 → 引导鲸鱼进食
}
// 2. 全特化(所有类型参数都特化:T1=Whale,T2=string)
template <> // 尖括号不可省略
void feed(Whale w, string food)
{
// 鲸鱼+字符串类型饲料的专属算法(如特殊饲料处理)
}
匹配优先级
- 非模板普通函数 > 模板特化版本 > 通用模板
- 若特化版本与通用模板冲突,可定义非模板函数重载,直接覆盖模板匹配
拓展
模板参数的默认值
C++11及以上支持为类型参数指定默认值,调用时可省略该参数的类型指定:
// 默认T2为int类型
template <typename T1, typename T2 = int>
void func(T1 a, T2 b = 0)
{
// 逻辑实现
}
// 调用示例
func<double>(3.14); // T1=double,T2默认int,b默认0
func<char, double>('a', 2.5); // 显式指定所有类型
非类型模板参数
模板参数不仅可以是“类型占位符”,还可以是“常量值”(非类型参数),需指定具体类型(如int、char、指针等):
// N为非类型模板参数(int类型常量)
template <<int N>
int sum()
{
return N * (N + 1) / 2; // 计算1~N的和
}
// 调用:尖括号中传递常量值
cout << sum<100>(); // 输出5050(1~100求和)
实参推断规则
- 编译器仅根据实参类型推导模板参数,不考虑返回值类型
- 若实参类型不匹配(如隐式转换无法完成),需显式指定模板类型
- 引用/指针类型的模板参数,推导时会保留const/volatile限定符:
template <typename T> void func(T& a); const int x = 10; func(x); // T推导为const int(保留const属性)
函数模板的局限性
- 算法必须统一:若不同类型的核心逻辑差异过大,特化/重载会增加复杂度,不如直接用普通函数
- 类型支持限制:模板依赖类型支持的操作(如
+、>),若类型不支持该操作(如自定义类未重载运算符),会编译报错 - 调试难度高:模板实例化在编译期完成,错误信息通常较为复杂,需熟悉模板语法才能定位问题
实践练习
练习需求
使用函数模板实现“找出三个数中的最大值并返回”,要求支持任意可比较类型(int、float、double等)
参考框架
#include <iostream>
using namespace std;
// 定义函数模板:返回三个参数中的最大值
template <typename T>
T maxThree(T a, T b, T c)
{
// 补全逻辑:先比较a和b,再与c比较,返回最大值
T maxVal = a > b ? a : b;
return maxVal > c ? maxVal : c;
}
// 测试代码
int main()
{
cout << maxThree(10, 20, 15) << endl; // 输出20(int类型)
cout << maxThree(3.14, 2.5, 5.6) << endl; // 输出5.6(double类型)
cout << maxThree('c', 'a', 'b') << endl; // 输出'c'(char类型)
return 0;
}
核心总结
- 模板核心价值:代码复用(算法一致时,避免多类型重载的冗余)
- 关键语法:
template <typename T>声明模板,尖括号传类型,圆括号传数据 - 三大特性:
- 重载:解决参数个数/算法不同的场景
- 特化:解决特定类型算法差异的场景
- 实例化:编译期根据调用生成具体函数
- 匹配优先级:普通函数 > 特化模板 > 通用模板

浙公网安备 33010602011771号