当前位置: 首页 > news >正文

C++泛型编程1 - 函数模板

C++函数模板完整教程

一、模板概述

模版是一种写程序的套路。是程序员写给编译器看的,程序员在编写时不用关心具体的数据类型,使用虚拟数据类型代替即可。只需要在使用时指定具体数据类型,编译器便会自动根据使用时的数据类型对模板进行实例化。即使使用时没有指定数据类型,编译器也会根据参数类型自动推导数据类型进行模板实例化。

特点:

  • 模板不可以直接使用,它只是一个框架。
  • 模板的通用并不是万能的。

分类

C++模板是泛型编程的基础,可以分为两大类:

  • 函数模板:用于生成通用函数
  • 类模板:用于生成通用类

1.1 模板核心优势

  • 类型安全:比宏更安全
  • 代码复用:避免为不同类型重写相同逻辑
  • 性能无损:编译时实例化,没有运行时开销
  • STL基础:标准模板库的构建基础

1.2 函数模板概念

函数模板(Function Templates)是C++泛型编程的重要特性,它允许我们编写与数据类型无关的通用函数。通过使用模板,我们可以用一套代码处理不同类型的数据,大大提高代码的重用性灵活性

1.3 函数模板的核心理念

  • 将数据类型参数化
  • 编译器在编译时自动生成特定版本的函数
  • 提高代码复用性可维护性

二、基本语法

1. 定义函数模板

template <typename T>
T func(parameters) {// 函数体
}
  • template <typename T>template <class T> 声明一个模板,T是类型参数
  • T 可以用任何有效标识符代替,但习惯上用T(Type)
  • 模板参数可以有多个:template <typename T1, typename T2>
  • T 可以作为参数类型、返回类型和函数体内用到的类型。

2. 简单示例

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

使用示例:

int main() {cout << maximum(10, 20) << endl;           // 调用int版本cout << maximum(3.14, 2.71) << endl;       // 调用double版本cout << maximum('A', 'B') << endl;         // 调用char版本return 0;
}

三、模板的定义与使用

1. 多类型参数

template <typename T1, typename T2>
void printPair(T1 first, T2 second) {cout << first << ", " << second << endl;
}

2. 默认类型参数

函数模板的类型参数也可以像函数参数一样指定默认值。

// 定义函数模板,给 T2 指定默认类型
template <typename T1, typename T2 = std::string>
void printPair(T1 first, T2 second) {std::cout << "First: " << first << ", Second: " << second << std::endl;
}int main() {// 1. 不指定类型。自动推导printPair(42, "Hello, C++");  // First: 42, Second: Hello, C++// 2. 部分指定类型(第二个参数保持默认 std::string)printPair<double>(3.14, "PI");  // First: 3.14, Second: PI// 3. 显式指定两个类型printPair<int, double>(10, 20.5);  // First: 10, Second: 20.5return 0;
}

默认类型参数不能在没有默认值的参数之前声明。
错误示范:template <typename T1 = int, typename T2>

3. 非类型参数模板

C++ 的模板不仅支持类型参数(typename T/class T),还支持非类型参数(如 int N、bool Flag、指针等)。我们可以用它们来:

  • 在编译时传递固定大小(如数组长度)
  • 控制代码生成(如开关优化)
  • 结合默认参数简化调用

以下是一个数组打印模板。

template <typename T, int size>
void printArray(T (&arr)[size]) {for(int i = 0; i < size; ++i) {cout << arr[i] << " ";}cout << endl;
}

该模板函数的特点是:

  1. template <typename T, int size> 声明了一个类型参数T和一个非类型参数size
  2. T (&arr)[size] 是一个数组引用参数,保留了数组的大小信息
  3. size不用显式传参,编译时自动确定。

要使用这个模板,必须满足以下条件:

  1. 必须传入实际的数组(不能是指针或vector等其他容器)
  2. 数组必须是固定大小的(不能是动态分配的数组)
  3. 数组大小必须在编译时已知
  4. 数组不能退化成指针(保持数组类型)

使用方法示例

int main() {int intArr[] = {1, 2, 3, 4, 5};double doubleArr[] = {1.1, 2.2, 3.3};printArray(intArr);    // 正确:T=int, size=5printArray(doubleArr); // 正确:T=double, size=3int* ptr = intArr;// printArray(ptr);    // 错误:传入的是指针不是数组std::vector<int> vec = {1, 2, 3};// printArray(vec);    // 错误:不适用于vector
}

技术原理

  1. 引用传递数组可以保留数组大小信息(sizeof(arr)/sizeof(arr[0])
  2. 模板会自动推导出数组的类型T和大小size
  3. 相比直接传指针,这种方式更安全,可以防止数组退化为指针
  4. 编译时就能确定数组大小,可以进行更好的优化

典型应用场景

  1. 需要处理原生数组的通用函数
  2. 需要知道数组大小的模板函数
  3. 避免数组到指针的隐式转换
  4. 保证类型安全的数组操作

这个模板是处理原生数组的一种类型安全方法,但只适用于编译时已知大小的真正数组。如果是动态数据结构,应考虑使用迭代器或容器方式。

4. 模板特化

作用:为特定类型提供特殊实现。

函数模板只有完全特化,不支持偏特化。

  • 偏特化:函数模板的部分类型参数实例化。
  • 全特化:函数模板的所有类型参数全部实例化。

定义方法:在函数名后加<>指定替代泛化类型的具体类型。

  template <>具体返回类型 函数名<具体类型列表>(具体类型参数列表){}

示例:

// 通用模板
template <typename T>
bool isEqual(T a, T b) {return a == b;
}// 为const char*类型的特化版本
template <>
bool isEqual<const char*>(const char* a, const char* b) {return strcmp(a, b) == 0;
}

四、函数模板与普通函数

1. 名称区别

  • 函数模板:有模板声明的函数,即使用template定义的函数。
  • 模板函数:是函数模板的实例化,使用函数模板调用的函数就是模板函数。
  • 普通函数: 非模版函数,没有模板声明,是有具体参数类型的函数。

2. 重载规则

当函数模板与普通函数同名时:

  1. 优先匹配普通函数
  2. 如果模板能产生更好的匹配,则选择模板
  3. 可以通过空模板参数列表强制使用模板:function<>(args)

模板函数与普通函数的优先级:

普通函数 > 模板函数(完全特化 > 偏特化 > 通用)

3. 示例

void print(int a) {cout << "普通函数: " << a << endl;
}template <typename T>
void print(T a) {cout << "模板函数: " << a << endl;
}int main() {print(10);      // 调用普通函数print<>(10);    // 强制调用模板函数print(10.5);    // 调用模板函数(更好的匹配)return 0;
}

五、注意事项与最佳实践

1. 注意事项

  1. 模板定义通常放在头文件中(因为编译时需要看到完整定义)
  2. 模板函数中使用的操作必须对所有可能的类型有效
  3. 模板编译错误通常在实例化时才会显现

2. 最佳实践

  1. 对于小型通用函数使用模板
  2. 遵循DRY原则(Don’t Repeat Yourself)
  3. 使用有意义的模板参数名(如T、U等)
  4. 当需要特殊处理某些类型时,使用模板特化

六、实际应用案例

1. 通用交换函数

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

2. 通用数组排序

template <typename T, int size>
void bubbleSort(T (&arr)[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]);}}}
}

3. 多类型组合函数

template <typename T1, typename T2>
auto multiply(T1 a, T2 b) -> decltype(a * b) {return a * b;
}

总结

模板定义

template <class T>
T func(T a);template <typename T, class E>
T func(T a, E b);template <typename T = int>
T func(T a);template <typename T, int size>
T func(T (&arr)[size]);

记忆重点

  • template 声明该函数或类是一个模板
  • typenameclass 声明一个泛化类型名称,可在类或函数中作为类型名使用,可以替代任意数据类型
  • <> 中定义泛化类型名称。称作类型参数列表。一般用TEO作为泛化类型名。
  • typename T = int 指定默认类型,在调模板函数时可以不指定。

使用

// 根据参数自动推导类型(隐式实例化),不一定调用函数模板
func(param);	
// 显式指定类型(显式实例化)	
func<int>(param);	
// 强制调用函数模板(隐式实例化)
func<>(param)    

函数模板是C++中强大的泛型编程工具,通过将类型参数化,可以创建更加灵活和可重用的代码。掌握函数模板可以帮助我们:

  1. 减少代码重复
  2. 提高类型安全性
  3. 增强代码可维护性
  4. 实现更通用的算法

从简单类型比较到复杂容器操作,函数模板在现代C++编程中都有广泛应用,是每个C++开发者必须掌握的核心技能。

http://www.lqws.cn/news/562663.html

相关文章:

  • PyQtNode Editor 第三篇创建节点(节点的定义)
  • 电子电气架构 --- 车辆产品的生产周期和研发周
  • 路由器对不同数据帧的处理
  • WebRTC(十一):RTCP和SRTCP
  • 黑客入门 | 用ROP和shellcode攻击SolarWinds Serv-U SSH漏洞
  • 【云桌面容器KasmVNC】如何关闭SSL使用HTTP
  • Pycatia二次开发基础代码解析:面属性控制、视图定向与特征统计的工业级实现
  • HashMap 和 ConcurrentHashMap的区别
  • 数据结构之——顺序栈与链式栈
  • 【图像处理基石】什么是摄影的数码味?
  • Redis—主从复制
  • 跟着AI学习C#之项目实战-电商平台 Day5
  • pandas 优雅处理值类型为list的列的csv读写问题
  • Day45 Tensorboard使用介绍
  • 《垒球百科》垒球有多重·垒球1号位
  • 在 RT-Thread 中实现 Shell 控制台的设计与源码剖析
  • C++入门(笔记)
  • MySQL 索引 -- 磁盘,主键索引,唯一索引,普通索引,全文索引
  • AC自动机 多模式字符串匹配(简单版)
  • 马斯克的 Neuralink:当意念突破肉体的边界,未来已来
  • 嵌入式原理与应用篇---ARM
  • 深度学习量化数值类型
  • 机器学习——线性回归
  • 数据结构与算法学习笔记(Acwing 提高课)----动态规划·单调队列优化DP
  • Requests源码分析:底层逻辑
  • 模板方法 + 策略接口
  • glog使用详解和基本使用示例
  • 数据结构:顺序表
  • Lua现学现卖
  • Java代码阅读题