
目录一、为什么需要模板二、函数模板基本语法多个类型参数函数模板的重载三、类模板基本语法成员函数类外定义多个模板参数四、实例化隐式 vs 显式隐式实例化最常见显式实例化隐式 vs 显式对比五、模板与继承模板类作为基类继承模板参数六、模板与友元模板类的友元函数所有实例共享的友元七、完整例子泛型动态数组八、常见错误1. 模板定义与实现分离2. 忘记 template 关键字3. 模板参数名冲突4. 依赖名称需要 typename九、这一篇的收获一、为什么需要模板写一个交换两个整数的函数cppvoid swapInt(int a, int b) { int temp a; a b; b temp; }如果要交换double、string、char……你得写 N 个几乎一样的函数cppvoid swapDouble(double a, double b) { double temp a; a b; b temp; } void swapString(string a, string b) { string temp a; a b; b temp; } // 无穷无尽...模板的解决方案写一次适用于所有类型。cpptemplate typename T void swapValues(T a, T b) { T temp a; a b; b temp; } // 使用 int x 5, y 10; swapValues(x, y); // T 被推导为 int double d1 1.2, d2 3.4; swapValues(d1, d2); // T 被推导为 double二、函数模板基本语法cpptemplate typename T T max(T a, T b) { return (a b) ? a : b; } // 使用 int m1 max(3, 5); // T 推导为 int double m2 max(3.14, 2.71); // T 推导为 double // max(3, 4.5); // ❌ 错误T 推导冲突int vs double多个类型参数cpptemplate typename T1, typename T2 T1 convert(const T2 val) { return static_castT1(val); } int i convertint(3.14); // 显式指定 T1T2 推导为 double函数模板的重载cpptemplate typename T void print(T value) { cout 通用版本: value endl; } void print(int value) { cout int 特化: value endl; } int main() { print(42); // 调用普通函数优先 print(3.14); // 调用模板无普通函数匹配 printint(42); // 显式调用模板版本 }三、类模板基本语法cpptemplate typename T class Stack { private: vectorT data; public: void push(const T val) { data.push_back(val); } T pop() { T top data.back(); data.pop_back(); return top; } bool empty() const { return data.empty(); } }; // 使用 Stackint intStack; intStack.push(10); intStack.push(20); cout intStack.pop(); // 20 Stackstring strStack; strStack.push(hello);成员函数类外定义cpptemplate typename T class Box { private: T content; public: void set(const T val); T get() const; }; // 类外定义需要重复 template 声明 template typename T void BoxT::set(const T val) { content val; } template typename T T BoxT::get() const { return content; }多个模板参数cpptemplate typename Key, typename Value class Pair { private: Key first; Value second; public: Pair(const Key k, const Value v) : first(k), second(v) {} Key getFirst() const { return first; } Value getSecond() const { return second; } }; Pairstring, int p(age, 25);四、实例化隐式 vs 显式隐式实例化最常见编译器根据使用方式自动生成代码cppStackint intStack; // 隐式实例化 Stackint Stackdouble dblStack; // 隐式实例化 Stackdouble每次用新类型实例化编译器都会生成一份完整代码。显式实例化提前告诉编译器生成特定类型的代码通常放在.cpp文件中cpp// 在 .cpp 文件中 template class Stackint; // 显式实例化 Stackint template class Stackdouble; // 显式实例化 Stackdouble // 函数模板显式实例化 template void swapValuesint(int, int);用途减少编译时间避免在多处隐式实例化将模板实现放在.cpp文件中而不是头文件发布库时只提供实例化后的类型隐式 vs 显式对比特性隐式实例化显式实例化代码位置使用处显式声明处编译时机使用时编译时确定头文件需要完整定义只需声明适用场景通用模板固定类型集合五、模板与继承模板类作为基类cpptemplate typename T class Base { protected: T value; public: Base(const T v) : value(v) {} virtual void print() const { cout Base: value endl; } }; // 派生类可以是普通类或模板类 class DerivedInt : public Baseint { public: DerivedInt(int v) : Baseint(v) {} void print() const override { cout DerivedInt: value endl; } }; template typename T class DerivedTemplate : public BaseT { public: DerivedTemplate(const T v) : BaseT(v) {} void print() const override { cout DerivedTemplate: BaseT::value endl; } };继承模板参数cpptemplate typename T class Container : public T { // 继承自模板参数 // 这是 Mixin 模式 };六、模板与友元友元函数/类也可以与模板配合。模板类的友元函数cpptemplate typename T class MyClass { private: T secret; public: MyClass(const T s) : secret(s) {} // 所有 MyClassT 实例的友元同类型 friend void showSecret(const MyClassT obj) { cout obj.secret endl; } }; int main() { MyClassint obj1(42); showSecret(obj1); // 42 MyClassstring obj2(hello); showSecret(obj2); // hello // showSecret(obj1) 和 showSecret(obj2) 是不同函数 }所有实例共享的友元cpptemplate typename T class MyClass { private: T secret; public: MyClass(const T s) : secret(s) {} // 所有 MyClass任意类型 的友元 template typename U friend void showAnySecret(const MyClassU obj); }; template typename U void showAnySecret(const MyClassU obj) { cout obj.secret endl; }七、完整例子泛型动态数组cpp#include iostream #include stdexcept using namespace std; template typename T class DynamicArray { private: T* data; size_t size; size_t capacity; void resize(size_t newCapacity) { T* newData new T[newCapacity]; for (size_t i 0; i size; i) { newData[i] data[i]; } delete[] data; data newData; capacity newCapacity; } public: DynamicArray() : data(nullptr), size(0), capacity(0) {} ~DynamicArray() { delete[] data; } // 拷贝构造深拷贝 DynamicArray(const DynamicArray other) : size(other.size), capacity(other.capacity) { data new T[capacity]; for (size_t i 0; i size; i) { data[i] other.data[i]; } } // 移动构造 DynamicArray(DynamicArray other) noexcept : data(other.data), size(other.size), capacity(other.capacity) { other.data nullptr; other.size 0; other.capacity 0; } void push_back(const T val) { if (size capacity) { resize(capacity 0 ? 1 : capacity * 2); } data[size] val; } T pop_back() { if (size 0) { throw out_of_range(数组为空); } return data[--size]; } T operator[](size_t index) { if (index size) { throw out_of_range(索引越界); } return data[index]; } const T operator[](size_t index) const { if (index size) { throw out_of_range(索引越界); } return data[index]; } size_t getSize() const { return size; } bool empty() const { return size 0; } // 友元输出运算符模板类友元 template typename U friend ostream operator(ostream os, const DynamicArrayU arr); }; template typename U ostream operator(ostream os, const DynamicArrayU arr) { os [; for (size_t i 0; i arr.size; i) { os arr.data[i]; if (i arr.size - 1) os , ; } os ]; return os; } int main() { DynamicArrayint intArr; intArr.push_back(10); intArr.push_back(20); intArr.push_back(30); cout intArr endl; cout pop: intArr.pop_back() endl; cout intArr endl; DynamicArraystring strArr; strArr.push_back(C); strArr.push_back(模板); cout strArr endl; // 拷贝构造 DynamicArraystring strArr2 strArr; cout 拷贝: strArr2 endl; return 0; }输出text[10, 20, 30] pop: 30 [10, 20] [C, 模板] 拷贝: [C, 模板]八、常见错误1. 模板定义与实现分离cpp// ❌ 错误将模板实现放在 .cpp 文件 // 编译器在隐式实例化时看不到定义导致链接错误 // 模板实现通常放在头文件中2. 忘记 template 关键字cpptemplate typename T void MyClassT::func() { ... } // ✅ 需要 templatetypename T3. 模板参数名冲突cpptemplate typename T class MyClass { T data; // 这里的 T 是模板参数 typedef int T; // ❌ 错误T 已被使用 };4. 依赖名称需要 typenamecpptemplate typename T void func() { T::iterator it; // ❌ 编译器不知道 iterator 是类型 typename T::iterator it2; // ✅ 告诉编译器这是一个类型 }九、这一篇的收获你现在应该理解函数模板template typename T定义类型自动推导或显式指定类模板Stackint实例化成员函数类外定义需重复模板声明实例化隐式使用时和显式提前声明模板与继承模板类可作为基类派生类可以是普通类或模板类模板与友元可以声明特定类型的友元或所有类型的友元 小作业实现一个OptionalT类模板类似std::optional的子集。支持hasValue()、value()、valueOr(default)、reset()。测试int、string、自定义类型。下一篇预告第42篇《模板特化与偏特化为特定类型定制实现》——泛型版本不能满足所有类型怎么办模板特化为特定类型提供专属实现偏特化为部分约束类型提供定制。下篇详解。