一、类模板的核心概念

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=double

2. 类模板 vs 函数模板

  • 类模板:生成整个类的多个实例(如 Container<int>Container<double> 是两个不同的类)。
  • 函数模板:生成函数的多个实例(如 max<int>max<double> 是两个不同的函数)。

二、类模板的语法与规则

1. 基础语法

template <typename T1, typename T2, ...>
class 类名 {
    // 类成员定义
};
  • typenameclass 等价,但推荐用 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(); // 输出 42

3. 偏特化的限制

  • 偏特化必须比主模板更“具体”。
  • 不能部分特化非类型参数(如 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 = 120

2. 类型萃取(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 需要 usingthis->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 双精度矩阵。