类函数与函数模板

类函数与函数模板
flowwalker类函数与函数模板
完全匹配的普通函数 > 完全匹配的模板函数 > 需要转换的普通函数 > 需要转换的模板函数
目录
[TOC]
泛型程序设计(Generic Programming)
算法实现时不指定具体要操作的数据的类型
泛型: 算法实现一遍 → 适用于多种数据结构→减少重复代码的编写
大量编写模板, 使用模板的程序设计
函数模板
类模板
函数模板
问题根源:排序或者更广地说,算法完全相同,但被排序数组元素的变量的类型声明不同
两种可能的解法
函数重载
- 同名函数
- 编译系统根据参数调用时实参类型,确定实际执行的函数
问题在于:要是类有无数,要重载无数
函数模板
1 template<class T>由编译系统根据sort函数调用时实参的类型,自动生成相应的模板函数
范式
单类型参数
1 | template<class T> |
多个类型参数
1 | template<class T1,class T2> |
函数模板的参数类型可以是
- 类型参数
- 基本数据类型,例如
...void print(T1 arg1,T2 arg2,string s){...}
类型参数可以用于函数模板的局部变量声明(就是在函数模板里调用类型参数),也可以用来作为返回值
二义性:
对于
Function(T arg1,T arg2),Function(1,1.0)——error:replace T with int or double?采用
Function(T1 arg1,T2 arg2)避免二义性⚠️显式指定**避免二义性
(⚠️如果可以自动匹配可以不指定)
int c=10; func(c); //自动匹配 func<double>(c); //注意:此处会发生隐式转换 func<int>(1,1.0); //ok,统一转化为int <!--code3-->
相关延伸
数组或者函数名作为值传入:
数组退化:退化为指向数组首元素的指针,
Type[N]退化为Type*函数退化:退化为指向函数的指针,
Ret(Args)退化为Ret(*)(Args)函数指针
Ret(*)(Args)函数类型
Ret(Args),实际传递通常退化举个例子 void foo(int)变成指针 void(*)(int), 具体写的指针是void(*p)(int),⬅️这个可以写在函数参数表里
一个细节:
auto p1 = foo; // 函数到指针转换,p1 是 void ()(int)
auto p2 = &foo; // 显式取地址,结果也是 void ()(int)//推导函数不隐式转化为指针 decltype(foo) x; // ✅ x 的类型是 void(int),不是指针 <!--code4-->
1
2
3
4
5
6
7
8 void foo(int a, int b) { }
void (*p)(int, int) = foo;
foo(1, 2); // 直接调用
p(1, 2); // 函数指针直接调用,编译器自动解引用
(*p)(1, 2); // 显式解引用后调用
(****p)(1, 2); // 甚至这样也行,多层解引用编译器自动归一化
类模板
目的:快速地定义出一批相似的类,例如数组类(包元素、基本操作)
特点:除了元素类型之外,其他完全相同
概念
在定义类的时候给它一个/多个参数, 这个/些参数表示不同的数据类型
在调用类模板时, 指定参数, 由编译系统根据参数提供的数据类型自动产生相应的模板类(类模板+指定具体数据类型)
范式
1 | template <class T> //关键就这一行,类模板的首部, 声明类模板的参数 |
More detail:
1 | template <class T1, class T2> |

模板类
定义
类模板A+指定具体类型数据1→模板类A1→生成该模板类的对象或指针
类模板A+指定具体类型数据2→模板类A2
…
类模板A+指定具体类型数据n→模板类An
类模板B+指定具体类型数据1→模板类B1
…
数据类型可以是类
同一个类模板的两个模板类是不兼容的(类名字相同,但是类型参数显然不同)(不能赋值、不能传指针/引用)
定义类模板的成员函数
小注:
arg→arguement 参数argc→argument count 参数数量argv→argument vector 参数数组
数组类模板实例
类模板声明
1 | //类模板的首部,声明类模板的参数 |
构造函数与析构函数实现
1 | //模板类CArray<T>的构造函数 |
定义类模板的成员函数
1 | //模板类CArray<T>的成员函数 len() |
函数模板作为类模板成员
成员函数模板只有调用时才会实例化
1 | template <class T> |
非类型参数
⚠️类模板的<类型参数表>中可以包含非类型参数
- 用来说明属性,常用于定义模板内常量,例如数组大小
- 类型参数则用来说明属性类型,例如成员函数参数类型和返回值
1 | template <class T,int size> |
⚠️ 类中并没有条件添加一个int size成员变量
类模板参数声明中的非类型参数→编译链接即确定→可以提高程序执行效率
继承与派生
1 | flowchart LR |
1. 类模板派生类模板
- 核心逻辑:父类是类模板,子类也保持模板特性,继承时必须传递父类的模板参数。
1 | // 父类:类模板 |
2. 模板类派生类模板
- 核心逻辑:父类是已经实例化的模板类(类型固定),子类可以是新的类模板。
1 | // 父类:模板类(已实例化,T=int固定) |
3. 普通类派生类模板
- 核心逻辑:父类是普通类,子类是类模板,继承时父类的类型需匹配子类模板参数。
1 | // 父类:普通类 |
4. 模板类派生普通类
- 核心逻辑:父类是模板类(类型固定),子类是普通类,直接继承固定的模板类实例。
1 | // 父类:模板类(已实例化) |
友元



static成员
- 类模板中可以定义静态成员, 那么从该类模板同一个实例化得到的模板类的所有对象, 都包含同样的静态成员

1 | //普通类静态成员初始化 |
模板特化(考试不要求)
一、函数模板的特化(考试不要求)
1. 核心语法与示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 // 通用函数模板(默认实现,适用于所有类型T)
template<class T>
void print(T x) {
cout << "Default Print: " << x << endl;
}
// 对bool类型的全特化(为特定类型提供专属实现)
template<>
void print(bool x) {
cout << (x ? "Good" : "Bad");
}
int main() {
print(5); // 匹配通用模板,输出:Default Print: 5
print("hello"); // 匹配通用模板,输出:Default Print: hello
print(5 == 1); // 匹配bool特化版本,输出:Bad
return 0;
}2. 关键说明
- 全特化:用
template<>标识,为完全确定的类型提供独立实现,调用优先级远高于通用模板。- 函数模板不支持部分特化:若需要针对某一类类型(如所有指针、所有容器)做定制,直接使用函数重载是更简洁、易维护的方案。
- 补充:函数模板特化本质是为通用模板的特定实例提供专属实现,而非重载(重载是不同的函数签名)。
二、类模板的特化(考试不要求)
类模板特化分为全特化和部分特化(偏特化),优先级规则:
全特化 > 部分特化 > 通用模板。1. 类模板全特化(完全指定所有模板参数)
为完全确定的模板参数提供专属实现,用
template<>标识。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 // 通用类模板(默认实现)
template<class T>
class Printer{
public:
void print(T x) {
cout << "Default Print: " << x << endl;
}
};
// 对bool类型的全特化
template<>
class Printer<bool>{
public:
void print(bool x) {
cout << (x ? "Good" : "Bad");
}
};
// 使用示例
Printer<int> p;
Printer<bool> p2;
p.print(5); // 调用通用模板,输出:Default Print: 5
p2.print(5); // 调用bool特化版本,输出:Good(5隐式转换为true)2. 类模板部分特化(仅指定部分/约束模板参数)
仅对多参数类模板生效,通过约束部分参数生成更具体的模板版本,适用于通用模板无法覆盖的场景。
- 通用模板定义:
1
2 template<class T1, class T2>
class MyClass {};
常见部分特化场景:
| 特化类型 | 语法 | 说明 |
| ---------------------- | ----------------------------------------- | ----------------------------------------- |
| 固定第二个参数 |template<class T> class MyClass<T, int>| 第二个参数固定为int,第一个参数仍为泛型 |
| 两个参数均为同类型指针 |template<class T> class MyClass<T*, T*>| 两个参数必须是同类型的指针 |
| 两个参数类型完全相同 |template<class T> class MyClass<T, T>| 两个参数必须是完全相同的类型 |全特化(完全指定所有参数):
1
2 template<>
class MyClass<int, double> {};三、核心对比与总结
特性 函数模板 类模板 全特化支持 ✅ 支持,用 template<>标识✅ 支持,用 template<>标识部分特化支持 ❌ 不支持,用函数重载替代 ✅ 支持,仅适用于多参数模板 调用优先级 特化版本 > 通用模板 全特化 > 部分特化 > 通用模板 适用场景 为特定类型定制函数逻辑 为特定类型/参数约束定制类的完整实现 四、补充注意事项
- 函数模板特化在工程中优先用函数重载:重载的可读性、可维护性远高于特化,且符合C++的设计习惯。
- 类模板特化的常见用途:为指针类型优化内存操作、为特定类型(如
std::string)定制成员函数、为特化版本单独初始化静态成员。- 部分特化仅针对模板参数的约束,而非具体类型:例如
MyClass<T*, T*>是针对所有指针类型的特化,而非某一个具体指针(如int*)。- 特化必须在通用模板声明之后定义,否则编译器无法识别特化关系。










