C++ 模板
一、模板概述
1.1 核心概念
模板是 C++ 泛型编程的核心机制,提供与类型无关的代码复用能力。
- 作用:一套代码适配多种数据类型,消除重复冗余代码
- 本质:编译期根据实际类型自动实例化生成对应代码,无运行时开销
- 两大分类:函数模板、类模板
- 典型应用:STL 容器(vector/map)、算法、迭代器
1.2 核心关键字
template:模板声明起始关键字typename/class:模板类型参数修饰,模板头中完全等价- 建议:泛型参数优先使用
typename
二、函数模板
2.1 基础语法
cpp
template <typename T>
返回值类型 函数名(参数列表)
{
// 通用逻辑,不依赖具体类型
}
2.2 基础实战案例:通用最大值
cpp
#include <iostream>
#include <string>
using namespace std;
template <typename T>
inline T const& Max(T const& a, T const& b)
{
return a < b ? b : a;
}
int main()
{
cout << Max(39, 20) << endl;
cout << Max(13.5, 20.7) << endl;
cout << Max(string("Hello"), string("World")) << endl;
return 0;
}
2.3 通用交换函数
cpp
#include <iostream>
using namespace std;
template<typename T>
void swapData(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
int main()
{
int a = 10, b = 20;
double x = 3.14, y = 6.66;
swapData(a, b);
swapData(x, y);
return 0;
}
2.4 多参数函数模板
cpp
#include <iostream>
using namespace std;
template<typename T1, typename T2>
void printTwoData(T1 a, T2 b)
{
cout << a << " | " << b << endl;
}
int main()
{
printTwoData(100, "C++模板");
printTwoData(9.99, false);
return 0;
}
2.5 函数模板调用方式
- 自动类型推导(默认):
swapData(a, b) - 手动显式指定类型:
swapData<int>(a, b)
2.6 函数模板限制
- 严格类型匹配,不支持隐式类型转换
- 参数类型不一致时,需手动强制转换或指定模板参数
三、类模板
3.1 基础语法
cpp
template <class T>
class 类名
{
public:
T 成员变量;
T 成员函数(T val);
};
3.2 通用栈类实战
cpp
#include <iostream>
#include <vector>
#include <stdexcept>
using namespace std;
template <class T>
class Stack
{
private:
vector<T> elems;
public:
void push(T const&);
void pop();
T top() const;
bool empty() const { return elems.empty(); }
};
// 类外实现成员函数必须携带模板头
template <class T>
void Stack<T>::push(T const& elem)
{
elems.push_back(elem);
}
template <class T>
void Stack<T>::pop()
{
if (empty()) throw out_of_range("栈为空");
elems.pop_back();
}
template <class T>
T Stack<T>::top() const
{
if (empty()) throw out_of_range("栈为空");
return elems.back();
}
int main()
{
Stack<int> intStack;
Stack<string> strStack;
return 0;
}
3.3 自定义通用数组类
cpp
#include <iostream>
using namespace std;
template<class T>
class MyArray
{
private:
T arr[100];
int size;
public:
MyArray() : size(0) {}
void add(T val) { arr[size++] = val; }
T get(int idx) { return arr[idx]; }
};
int main()
{
MyArray<int> intArr;
MyArray<string> strArr;
intArr.add(666);
strArr.add("模板编程");
return 0;
}
3.4 类模板默认参数
cpp
#include <iostream>
using namespace std;
// 设置默认类型为 int
template<class T = int>
class Demo
{
public:
T num;
};
int main()
{
Demo<> d1; // 使用默认 int
Demo<double> d2; // 自定义类型
return 0;
}
四、模板特化
4.1 全特化
对单一具体类型完全重写模板实现,优先级最高。
cpp
#include <iostream>
#include <cstring>
using namespace std;
// 通用模板
template<class T>
class Compare
{
public:
bool isEqual(T a, T b)
{
return a == b;
}
};
// char* 全特化版本
template<>
class Compare<char*>
{
public:
bool isEqual(char* a, char* b)
{
return strcmp(a, b) == 0;
}
};
4.2 偏特化(局部特化)
仅约束部分参数、指针、const 等修饰,仅类模板支持偏特化。
cpp
#include <iostream>
using namespace std;
// 通用模板
template <typename T>
class DataDeal
{
public:
void print() { cout << "普通类型处理" << endl; }
};
// 指针类型偏特化
template <typename T>
class DataDeal<T*>
{
public:
void print() { cout << "指针类型偏特化处理" << endl; }
};
int main()
{
DataDeal<int> d1;
DataDeal<int*> d2;
d1.print();
d2.print();
return 0;
}
4.3 特化优先级
全特化 > 偏特化 > 通用模板
五、可变参数模板(C++11)
5.1 语法说明
typename... Args:可变参数包args...:参数包展开
5.2 递归展开打印案例
cpp
#include <iostream>
using namespace std;
// 递归终止
void printArgs()
{
cout << endl;
}
// 可变参数模板
template<typename T, typename... Args>
void printArgs(T first, Args... args)
{
cout << first << " ";
printArgs(args...);
}
int main()
{
printArgs(10, 3.14, "C++", 'A', true);
return 0;
}
5.3 可变参数类模板
cpp
template<typename... Args>
class TupleWrap;
template<typename T, typename... Args>
class TupleWrap<T, Args...>
{
private:
T val;
TupleWrap<Args...> next;
public:
TupleWrap(T v, Args... rest) : val(v), next(rest...) {}
void show() { cout << val << " "; next.show(); }
};
template<>
class TupleWrap<> { public: void show(){} };
六、模板继承
6.1 普通类继承类模板
cpp
#include <iostream>
using namespace std;
template<typename T>
class Base
{
protected:
T data;
public:
void setData(T d) { data = d; }
T getData() { return data; }
};
// 普通子类指定具体类型
class Son : public Base<int>
{
public:
void func() { cout << getData() << endl; }
};
6.2 模板继承模板(泛型继承)
cpp
#include <iostream>
using namespace std;
template<typename T>
class Parent
{
protected:
T num;
};
template<typename T>
class Child : public Parent<T>
{
public:
// 依赖名称必须加 this->
void setNum(T n) { this->num = n; }
void show() { cout << this->num << endl; }
};
关键规则:模板继承中访问父类成员,必须加
this->消除编译器依赖歧义。
七、函数模板重载与特化
7.1 重载规则
普通函数 > 模板特化 > 通用模板
cpp
#include <iostream>
using namespace std;
template<typename T>
void func(T t) { cout << "通用模板" << endl; }
// 普通函数重载
void func(int t) { cout << "普通函数" << endl; }
// 全特化
template<>
void func<double>(double d) { cout << "double 特化" << endl; }
八、分离编译问题(工程重点)
8.1 问题现象
类模板头文件声明、cpp 文件实现,编译正常,链接报错。
8.2 根本原因
模板为延迟实例化,仅在被使用时生成代码;
单独编译 cpp 文件不会实例化模板,链接时找不到函数实现。
8.3 三种解决方案
- 推荐:模板声明与实现全部写在头文件
- 头文件末尾引入实现文件:
#include "xxx.tpp" - 大型项目:显式实例化指定类型
九、核心特性与易错点
9.1 模板核心特性
- 编译期实例化,零运行时开销
- 强类型安全,杜绝裸指针泛型风险
- 多类型实例化会产生代码膨胀
- 不支持运行时动态类型
9.2 高频易错点
- 类模板必须显式指定类型,无自动推导
- 模板外部成员函数必须重写
template头 - 嵌套依赖类型必须使用
typename修饰 - 模板继承成员访问必须加
this-> - 禁止将模板实现拆分到 cpp 文件
十、总结
- 函数模板:适配通用算法,自动类型推导
- 类模板:构建泛型容器、通用数据结构
- 特化:为特殊类型定制逻辑,优化兼容
- 可变参数模板:实现万能参数、日志、tuple 等高级能力
- 工程规范:模板代码统一存放头文件,规避链接错误