模板是 C++ 泛型编程的核心:写一份代码,让它适用于任意类型。STL 的 vector<int>vector<string> 背后全是模板。掌握模板,你才能看懂标准库,也才能写出真正的通用代码。

1. 为什么需要模板

没有模板时,想支持 int 和 double 的加法,得复制两份几乎相同的函数。模板把"类型"变成参数,编译器在编译期按需生成对应版本:

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

int main() {
    cout << my_max(3, 7);          // T 推导为 int
    cout << my_max(3.5, 2.1);     // T 推导为 double
    cout << my_max('a', 'z');     // T 推导为 char
}

2. 函数模板

typenameclass 在这里等价。多个参数类型可以不同:

template <typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {   // C++11 尾置返回类型
    return a + b;
}
// 更简洁:C++14 起直接用 auto
template <typename T, typename U>
auto add2(T a, U b) { return a + b; }

// 显式指定类型(模板参数无法推导时)
auto v = my_max<double>(3, 2.5);

3. 类模板

类模板让"容器"成为可能。下面实现一个迷你栈:

template <typename T>
class MiniStack {
    vector<T> data;
public:
    void push(const T& v) { data.push_back(v); }
    void pop() { data.pop_back(); }
    T& top() { return data.back(); }
    bool empty() const { return data.empty(); }
};

MiniStack<int> s1;
MiniStack<string> s2;   // 同一份代码,两种类型

注意:类模板的成员函数定义在类外时要重复写 template <typename T>。由于模板要"看到完整定义才能实例化",实现通常直接写在头文件里。

4. 模板特化

某些类型需要特殊处理时,可以特化。例如 const char* 的比较不能用 >(那是在比地址):

// 全特化:为特定类型提供专用版本
template <>
const char* my_max(const char* a, const char* b) {
    return strcmp(a, b) > 0 ? a : b;   // 按内容比较
}

5. 非类型模板参数

模板参数不一定是类型,也可以是编译期常量(整数、指针等),常用于固定大小:

template <typename T, size_t N>
class FixedArray {
    T arr[N];              // N 是编译期常量
public:
    size_t size() const { return N; }
    T& operator[](size_t i) { return arr[i]; }
};

FixedArray<int, 8> buf;    // 8 个 int 的定长数组

6. 模板与 STL 的关系

STL 组件模板类型例子
容器类模板vector<T>, map<K,V>
算法函数模板sort(begin, end)
迭代器类模板vector<T>::iterator
函数对象类模板greater<T>, less<T>
💡 模板报错信息又长又吓人,这是正常的。遇到编译错误,从第一行"error"往前看,一般真正的错误在文件头部。建议练习:自己写一个 my_vector<T>,你就懂了为什么 STL 头文件里全是代码。