【C++模板与泛型编程】模板定义

发布时间:2026/7/29 7:23:56
【C++模板与泛型编程】模板定义
在 C 编程中模板是实现泛型编程的核心机制。通过模板我们可以创建通用的函数、类和算法而不必预先指定具体的数据类型。通过模板我们可以编写更加灵活、复用性更高的代码极大地提升开发效率。一、函数模板的定义与使用1.1 基本概念函数模板允许我们定义一个通用的函数其参数类型和返回值类型可以是泛型的。语法格式如下代码语言javascriptAI代码解释template typename T T max(T a, T b) { return a b ? a : b; }其中typename关键字也可以用class替代二者在模板定义中没有区别。template typename T声明了一个模板参数T它可以是任何类型。函数max可以接受两个类型为T的参数并返回类型为T的结果。1.2 模板参数推导当调用函数模板时编译器会根据传入的实参自动推导模板参数的类型代码语言javascriptAI代码解释int a 5, b 10; int result max(a, b); // 自动推导 T 为 int double x 3.14, y 2.71; double d_result max(x, y); // 自动推导 T 为 double1.3 显式指定模板参数在某些情况下编译器无法自动推导模板参数此时需要显式指定代码语言javascriptAI代码解释// 显式指定 T 为 double auto mixed_result maxdouble(a, x); // int 和 double 的混合比较1.4 函数模板示例通用交换函数下面是一个通用的交换函数模板它可以交换任意类型的两个变量代码语言javascriptAI代码解释template typename T void swap(T a, T b) { T temp a; a b; b temp; } // 使用示例 int main() { int x 5, y 10; swap(x, y); // 交换两个 int double a 3.14, b 2.71; swap(a, b); // 交换两个 double std::string s1 hello, s2 world; swap(s1, s2); // 交换两个 string return 0; }二、类模板的定义与使用2.1 基本概念类模板允许我们定义一个通用的类其中的成员变量和成员函数可以使用泛型类型。语法格式如下代码语言javascriptAI代码解释template typename T class Container { private: T value; public: Container(T val) : value(val) {} T getValue() const { return value; } void setValue(T val) { value val; } };2.2 实例化类模板使用类模板时必须显式指定模板参数的类型代码语言javascriptAI代码解释Containerint intContainer(42); // T 被实例化为 int Containerdouble doubleContainer(3.14); // T 被实例化为 double2.3 类模板的成员函数类模板的成员函数可以在类内部定义也可以在类外部定义。在类外部定义时需要使用完整的模板声明代码语言javascriptAI代码解释template typename T class Container { private: T value; public: Container(T val); T getValue() const; void setValue(T val); }; // 类外定义构造函数 template typename T ContainerT::Container(T val) : value(val) {} // 类外定义成员函数 template typename T T ContainerT::getValue() const { return value; } template typename T void ContainerT::setValue(T val) { value val; }2.4 类模板示例动态数组下面是一个简单的动态数组类模板实现代码语言javascriptAI代码解释template typename T class DynamicArray { private: T* data; size_t size; size_t capacity; public: DynamicArray() : data(nullptr), size(0), capacity(0) {} ~DynamicArray() { delete[] data; } void push_back(const T value) { if (size capacity) { resize(capacity 0 ? 1 : capacity * 2); } data[size] value; } T operator[](size_t index) { return data[index]; } const T operator[](size_t index) const { return data[index]; } size_t getSize() const { return size; } private: 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; } }; // 使用示例 int main() { DynamicArrayint intArray; intArray.push_back(10); intArray.push_back(20); std::cout Array size: intArray.getSize() std::endl; std::cout Element at index 0: intArray[0] std::endl; DynamicArraystd::string stringArray; stringArray.push_back(Hello); stringArray.push_back(World); std::cout Element at index 1: stringArray[1] std::endl; return 0; }三、模板形参详解3.1 模板类型形参模板类型形参是最常见的模板参数使用typename或class关键字声明代码语言javascriptAI代码解释template typename T // typename 和 class 在这里等价 class MyClass {};3.2 非类型模板形参非类型模板形参允许我们传递一个常量值作为模板参数。常见的非类型参数包括整数、指针和引用代码语言javascriptAI代码解释template int N class FixedArray { private: int data[N]; public: int operator[](int index) { return data[index]; } const int operator[](int index) const { return data[index]; } int size() const { return N; } }; // 使用示例 FixedArray10 arr; // 创建一个大小为 10 的数组3.3 模板模板形参模板模板形参允许将一个模板作为另一个模板的参数代码语言javascriptAI代码解释template template typename class Container, typename T class Wrapper { private: ContainerT container; public: void add(const T value) { container.push_back(value); } }; // 使用示例 #include vector #include list Wrapperstd::vector, int vecWrapper; // 使用 vector 作为容器 Wrapperstd::list, double listWrapper; // 使用 list 作为容器3.4 模板参数默认值模板参数可以有默认值类似于函数参数的默认值代码语言javascriptAI代码解释template typename T int, int N 10 class DefaultArray { private: T data[N]; public: // ... }; // 使用示例 DefaultArray defaultArray; // 使用默认参数int, 10 DefaultArraydouble, 20 customArray; // 自定义参数四、重载操作符与模板结合4.1 操作符重载基础操作符重载允许我们为自定义类型重新定义操作符的行为。例如为自定义类重载操作符代码语言javascriptAI代码解释class Complex { private: double real, imag; public: Complex(double r 0, double i 0) : real(r), imag(i) {} // 重载 操作符 Complex operator(const Complex other) const { return Complex(real other.real, imag other.imag); } };4.2 模板与操作符重载结合当操作符重载与模板结合时可以为泛型类提供通用的操作符行为代码语言javascriptAI代码解释template typename T class Pair { private: T first, second; public: Pair(const T a, const T b) : first(a), second(b) {} // 重载 操作符 Pair operator(const Pair other) const { return Pair(first other.first, second other.second); } };4.3 友元函数与模板友元函数可以与模板结合实现更灵活的操作符重载代码语言javascriptAI代码解释template typename T class Point { private: T x, y; public: Point(T x 0, T y 0) : x(x), y(y) {} // 声明友元函数 friend Point operator(const Point a, const Point b) { return Point(a.x b.x, a.y b.y); } };五、类型转换与模板5.1 隐式类型转换模板类可以定义转换构造函数实现从其他类型到模板类型的隐式转换代码语言javascriptAI代码解释template typename T class SmartPtr { private: T* ptr; public: // 转换构造函数 template typename U SmartPtr(U* p) : ptr(p) {} // ... }; // 使用示例 class Base {}; class Derived : public Base {}; SmartPtrBase ptr(new Derived); // 隐式转换5.2 类型转换操作符模板类也可以定义类型转换操作符实现从模板类型到其他类型的转换代码语言javascriptAI代码解释template typename T class Number { private: T value; public: Number(T val) : value(val) {} // 类型转换操作符 template typename U operator U() const { return static_castU(value); } }; // 使用示例 Numberint num(42); double d num; // 隐式转换为 double六、编写泛型程序的最佳实践6.1 遵循最小特权原则模板应该只依赖于完成任务所需的最小接口提高代码的通用性代码语言javascriptAI代码解释// 不好的设计依赖具体类型的接口 template typename T void printAll(const std::vectorT vec) { for (const auto elem : vec) { elem.print(); // 依赖 T 有 print() 方法 } } // 好的设计只依赖必要的操作符 template typename T void printAll(const std::vectorT vec) { for (const auto elem : vec) { std::cout elem std::endl; // 只依赖 操作符 } }6.2 使用概念Concepts约束模板C20 引入的概念Concepts可以约束模板参数必须满足的条件代码语言javascriptAI代码解释// C20 概念示例 template typename T concept Addable requires(T a, T b) { { a b } - std::same_asT; }; template Addable T T sum(T a, T b) { return a b; }6.3 避免模板代码膨胀过度使用模板可能导致代码膨胀可以通过显式实例化和模板复用减少代码体积代码语言javascriptAI代码解释// 显式实例化 template class std::vectorint; // 只在一处实例化 vectorint七、实战案例实现一个通用矩阵类下面是一个使用模板实现的通用矩阵类展示了模板、操作符重载和类型转换的综合应用代码语言javascriptAI代码解释#include iostream template typename T, size_t Rows, size_t Cols class Matrix { private: T data[Rows][Cols]; public: // 默认构造函数 Matrix() { for (size_t i 0; i Rows; i) { for (size_t j 0; j Cols; j) { data[i][j] T(); } } } // 元素访问 T operator()(size_t i, size_t j) { return data[i][j]; } const T operator()(size_t i, size_t j) const { return data[i][j]; } // 矩阵加法 template typename U MatrixT, Rows, Cols operator(const MatrixU, Rows, Cols other) const { MatrixT, Rows, Cols result; for (size_t i 0; i Rows; i) { for (size_t j 0; j Cols; j) { result(i, j) data[i][j] other(i, j); } } return result; } // 矩阵乘法需要满足列数等于另一个矩阵的行数 template size_t OtherCols MatrixT, Rows, OtherCols operator*(const MatrixT, Cols, OtherCols other) const { MatrixT, Rows, OtherCols result; for (size_t i 0; i Rows; i) { for (size_t j 0; j OtherCols; j) { for (size_t k 0; k Cols; k) { result(i, j) data[i][k] * other(k, j); } } } return result; } // 添加打印函数 void print() const { for (size_t i 0; i Rows; i) { for (size_t j 0; j Cols; j) { std::cout data[i][j] \t; } std::cout std::endl; } } };使用示例代码语言javascriptAI代码解释int main() { Matrixint, 2, 2 m1; m1(0, 0) 1; m1(0, 1) 2; m1(1, 0) 3; m1(1, 1) 4; Matrixint, 2, 2 m2; m2(0, 0) 5; m2(0, 1) 6; m2(1, 0) 7; m2(1, 1) 8; std::cout Matrix m1: std::endl; m1.print(); std::cout \nMatrix m2: std::endl; m2.print(); Matrixint, 2, 2 sum m1 m2; std::cout \nm1 m2: std::endl; sum.print(); Matrixint, 2, 2 product m1 * m2; std::cout \nm1 * m2: std::endl; product.print(); return 0; }八、总结模板是 C 中最强大的特性之一它使得我们能够编写高度通用、灵活的代码。通过函数模板和类模板