C++ 函数模板
函数模板(Function Template) 是C++中实现泛型编程的核心工具,它允许编写与类型无关的通用代码。通过函数模板,你可以定义一个函数蓝图,编译器会根据实际使用的类型自动生成具体的函数代码。
基本概念
1. 模板定义
cpp
// 定义一个函数模板 template <typename T> // T 是类型参数 T max(T a, T b) { return (a > b) ? a : b; } // 或者使用 class 关键字(和 typename 等价) template <class T> T min(T a, T b) { return (a < b) ? a : b; }
2. 模板使用
cpp
int main() { int i1 = 10, i2 = 20; double d1 = 3.14, d2 = 2.71; char c1 = 'A', c2 = 'B'; // 编译器自动推导类型并生成具体函数 cout << max(i1, i2) << endl; // 生成 max<int>(int, int) cout << max(d1, d2) << endl; // 生成 max<double>(double, double) cout << max(c1, c2) << endl; // 生成 max<char>(char, char) return 0; }
模板参数
1. 类型参数
cpp
// 单个类型参数 template <typename T> void print(T value) { cout << value << endl; } // 多个类型参数 template <typename T1, typename T2> auto add(T1 a, T2 b) -> decltype(a + b) { return a + b; } // 使用 print(42); // T = int print("Hello"); // T = const char* add(3, 4.5); // T1 = int, T2 = double
2. 非类型参数
模板参数也可以是值(整数、指针、引用等):
cpp
// 数组大小作为模板参数 template <typename T, int size> class FixedArray { private: T data[size]; public: T& operator[](int index) { return data[index]; } }; // 使用 FixedArray<int, 10> arr; // 创建包含10个int的数组 // 函数模板中的非类型参数 template <typename T, int N> void fillArray(T (&arr)[N], T value) { for (int i = 0; i < N; i++) { arr[i] = value; } }
模板实例化
1. 隐式实例化
编译器根据调用自动推导类型:
cpp
template <typename T> T square(T x) { return x * x; } int main() { square(5); // 实例化为 square<int>(int) square(3.14); // 实例化为 square<double>(double) return 0; }
2. 显式实例化
明确指定模板参数:
cpp
template <typename T> void process(T data) { // ... } int main() { process<int>(42); // 显式指定T为int process<double>(3.14); // 显式指定T为double // 即使可以推导,也可以显式指定 process<string>("hello"); // 显式指定T为string return 0; }
函数模板与重载
1. 模板重载
可以定义多个同名的函数模板:
cpp
// 处理单个参数 template <typename T> void print(T value) { cout << "单个值: " << value << endl; } // 处理两个参数 template <typename T1, typename T2> void print(T1 a, T2 b) { cout << "两个值: " << a << ", " << b << endl; } // 处理数组(特化形式) template <typename T, int N> void print(T (&arr)[N]) { cout << "数组: "; for (int i = 0; i < N; i++) { cout << arr[i] << " "; } cout << endl; }
2. 模板与普通函数重载
当模板函数和普通函数重名时:
cpp
// 普通函数 void print(int value) { cout << "普通函数: " << value << endl; } // 模板函数 template <typename T> void print(T value) { cout << "模板函数: " << value << endl; } int main() { print(10); // 优先调用普通函数(精确匹配) print(3.14); // 调用模板函数(生成print<double>) print('A'); // 调用模板函数(生成print<char>) print<>(10); // 强制调用模板函数(print<int>) return 0; }
模板特化
为特定类型提供特殊实现:
cpp
// 通用模板 template <typename T> bool isEqual(T a, T b) { return a == b; } // 特化:针对char*类型 template <> bool isEqual<char*>(char* a, char* b) { return strcmp(a, b) == 0; } // 特化:针对double类型(处理浮点数精度问题) template <> bool isEqual<double>(double a, double b) { return fabs(a - b) < 1e-6; }
模板参数推导与auto
1. 自动类型推导
cpp
template <typename T> auto getValue(T container) -> decltype(container.front()) { return container.front(); } // C++14及以后可以更简洁 template <typename T> auto getValue(T container) { return container.front(); }
2. 使用auto作为参数(C++20)
cpp
// C++20引入的简写函数模板 auto print(auto value) { // 等价于 template<typename T> void print(T value) cout << value << endl; } auto max(auto a, auto b) { // 可以处理不同类型参数 return (a > b) ? a : b; }
实战示例
示例1:通用交换函数
cpp
template <typename T> void swapValues(T& a, T& b) { T temp = a; a = b; b = temp; } int main() { int x = 10, y = 20; swapValues(x, y); // 交换整数 string s1 = "Hello", s2 = "World"; swapValues(s1, s2); // 交换字符串 return 0; }
示例2:查找数组中的最大值
cpp
template <typename T, int N> T findMax(T (&arr)[N]) { T maxVal = arr[0]; for (int i = 1; i < N; i++) { if (arr[i] > maxVal) { maxVal = arr[i]; } } return maxVal; } int main() { int intArr[] = {1, 5, 3, 9, 2}; cout << findMax(intArr) << endl; // 输出 9 double doubleArr[] = {1.1, 5.5, 3.3, 9.9, 2.2}; cout << findMax(doubleArr) << endl; // 输出 9.9 return 0; }
示例3:通用排序函数
cpp
template <typename T> void bubbleSort(T arr[], int size) { for (int i = 0; i < size - 1; i++) { for (int j = 0; j < size - i - 1; j++) { if (arr[j] > arr[j + 1]) { swapValues(arr[j], arr[j + 1]); } } } }
注意事项与最佳实践
1. 模板代码放在头文件中
由于模板是在编译时实例化的,通常需要将模板的完整定义放在头文件中。
2. 类型约束(C++20概念)
cpp
// C++20之前:缺乏类型约束 template <typename T> T add(T a, T b) { return a + b; // 如果T不支持+操作符,会在实例化时报错 } // C++20:使用概念约束 template <typename T> requires std::is_arithmetic_v<T> // 约束T必须是算术类型 T add(T a, T b) { return a + b; }
3. 避免代码膨胀
过多不同类型的实例化可能会导致代码体积增大。
4. 编译时错误信息
模板的错误信息通常比较晦涩,因为错误在实例化时才被发现。
函数模板 vs 函数重载
| 特性 | 函数模板 | 函数重载 |
|---|---|---|
| 目的 | 编写类型无关的通用代码 | 为不同类型提供不同实现 |
| 代码量 | 一份模板代码,多份实例 | 每个重载都需要单独实现 |
| 类型检查 | 实例化时检查 | 定义时检查 |
| 适用场景 | 算法相同,仅类型不同 | 不同类型需要不同处理逻辑 |
总结
- 函数模板是实现泛型编程的基础
- 通过
template <typename T>声明模板参数 - 编译器在编译时根据实际类型生成具体代码
- 支持类型参数和非类型参数
- 可以与函数重载结合使用
- 提供模板特化机制处理特殊类型
- C++20引入了更简洁的auto参数和概念约束
函数模板极大地提高了代码的复用性,是STL(标准模板库)的基石,使得编写类型安全且高效的通用算法成为可能。