一、类模板的核心概念
1. 什么是类模板?
类模板是泛型编程的基础,允许编写一个通用类,自动适配多种类型,避免重复代码。
例如,一个通用的 Container 类:
template <typename T>
class Container {
T value;
public:
Container(T v) : value(v) {}
T get() const { return value; }
};调用时:
Container<int> intContainer(42); // T=int
Container<double> doubleContainer(3.14); // T=double2. 类模板 vs 函数模板
- 类模板:生成整个类的多个实例(如
Container<int>和Container<double>是两个不同的类)。 - 函数模板:生成函数的多个实例(如
max<int>和max<double>是两个不同的函数)。
二、类模板的语法与规则
1. 基础语法
template <typename T1, typename T2, ...>
class 类名 {
// 类成员定义
};typename与class等价,但推荐用typename。- 模板参数可有默认值(C++11 起):
template <typename T = int>
class DefaultContainer { /* ... */ };2. 成员函数的延迟实例化
- 类模板的成员函数只有在被调用时才会实例化。
template <typename T>
class MyClass {
public:
void foo() { /* 实现依赖 T */ }
void bar() { /* 实现不依赖 T */ }
};- 如果只调用
bar(),foo()不会被实例化。
3. 静态成员
- 每个模板实例拥有独立的静态成员:
template <typename T>
class Counter {
public:
static int count;
};
template <typename T> int Counter<T>::count = 0; // 静态成员定义
Counter<int>::count++; // Counter<int>::count = 1
Counter<double>::count++; // Counter<double>::count = 1三、类模板的特化与偏特化
1. 全特化(Full Specialization)
为特定类型提供定制实现:
template <>
class Container<std::string> {
std::string value;
public:
Container(std::string v) : value(v) {}
std::string get() const { return "String: " + value; }
};调用:
Container<std::string> strContainer("Hello");
std::cout << strContainer.get(); // 输出 "String: Hello"2. 偏特化(Partial Specialization)
为满足某些条件的类型提供定制实现(如指针类型、容器类型):
template <typename T>
class Container<T*> {
T* ptr;
public:
Container(T* p) : ptr(p) {}
T* get() const { return ptr; }
};调用:
int x = 42;
Container<int*> ptrContainer(&x);
std::cout << *ptrContainer.get(); // 输出 423. 偏特化的限制
- 偏特化必须比主模板更“具体”。
- 不能部分特化非类型参数(如
int N)。
四、模板参数的类型
1. 类型参数(Type Parameters)
template <typename T>
class MyVector { /* ... */ };2. 非类型参数(Non-Type Parameters)
传递常量值(如整数、指针):
template <typename T, int Size>
class StaticArray {
T data[Size];
public:
T& operator[](int i) { return data[i]; }
};调用:
StaticArray<int, 10> arr;
arr[5] = 42;3. 模板模板参数(Template Template Parameters)
将模板作为参数(常用于容器适配器):
template <typename T, template <typename> class Container>
class Wrapper {
Container<T> data;
public:
void add(const T& val) { data.push_back(val); }
};调用:
Wrapper<int, std::vector> w; // 使用 std::vector 作为底层容器五、类模板的显式实例化与分离编译
1. 显式实例化(Explicit Instantiation)
在编译时生成特定类型的模板实例:
template class Container<int>; // 显式实例化 Container<int>- 用于避免多次实例化,减少编译时间。
2. 分离编译(Separate Compilation)
- 问题:模板定义必须放在头文件中(编译器需要看到完整定义才能实例化)。
- 解决:将模板声明与实现放在同一头文件,或使用
.tpp文件包含实现。
六、类模板的继承与组合
1. 基类是模板
template <typename T>
class Base {
public:
void foo() { /* ... */ }
};
template <typename T>
class Derived : public Base<T> {
// 需要显式访问基类成员
using Base<T>::foo;
};2. 派生类是模板
template <typename T>
class Derived : public BaseClass {
// 基类是固定类型
};3. CRTP(奇异递归模板模式)
基类通过模板参数继承派生类:
template <typename Derived>
class Base {
public:
void interface() {
static_cast<Derived*>(this)->implementation(); // 静态多态
}
};
class Derived : public Base<Derived> {
void implementation() { /* ... */ }
};七、类模板的高级技巧
1. 模板元编程(TMP)
利用模板在编译期计算值:
template <int N>
struct Factorial {
static const int value = N * Factorial<N - 1>::value;
};
template <>
struct Factorial<0> {
static const int value = 1;
};
// 调用
int x = Factorial<5>::value; // x = 1202. 类型萃取(Type Traits)
通过 std::enable_if 控制模板启用条件:
template <typename T>
class Container {
static_assert(std::is_integral<T>::value, "T must be integral");
// ...
};3. 别名模板(Alias Templates)
为复杂模板类型定义别名:
template <typename T>
using Vec = std::vector<std::pair<T, T>>;
Vec<int> v; // 等价于 std::vector<std::pair<int, int>>八、常见陷阱与解决方案
1. 类型匹配失败
- 问题:模板参数类型不匹配导致编译错误。
- 解决:显式指定模板参数或使用
static_cast。
2. 偏特化语法错误
- 问题:偏特化未比主模板更具体。
- 解决:确保偏特化条件更严格。
3. 代码膨胀(Code Bloat)
- 问题:每个类型生成独立实例,增加代码体积。
- 优化:提取公共逻辑到非模板基类或工具函数。
4. 模板依赖问题
- 问题:模板依赖未正确声明(如
Base<T>::foo需要using或this->foo)。 - 解决:显式指定依赖项。
九、实际应用场景
1. 标准库容器
std::vector<T>,std::map<K, V>等。- 支持自定义分配器:
std::vector<int, MyAllocator>。
2. 智能指针
std::shared_ptr<T>:管理动态分配的对象。
3. 策略模式
通过模板参数注入行为:
template <typename Policy>
class Logger : public Policy {
public:
void log(const std::string& msg) {
Policy::write(msg); // 调用策略的具体实现
}
};4. 数值计算库
- 矩阵类:
Matrix<double, 3, 3>表示 3x3 双精度矩阵。