C++模板入门:函数与类模板详解

张开发
2026/4/15 6:17:14 15 分钟阅读

分享文章

C++模板入门:函数与类模板详解
C模板是一种支持泛型编程的语言特性它允许编写与类型无关的代码从而提高代码的复用性和灵活性。模板的核心思想是“参数化类型”即让数据类型本身成为函数或类的参数。C模板主要分为函数模板和类模板两大类。1. 函数模板函数模板用于定义一系列逻辑相同但操作数据类型不同的函数。其基本语法如下template typename T // 或 template class T T functionName(T param) { // 函数体 }template关键字声明一个模板。typename T或class T定义了一个类型参数T它代表一个占位符类型在调用时会被具体的类型如int,double,string等替换。函数返回值、参数类型或函数体内都可以使用这个类型参数T。示例一个返回两数中较大值的函数模板#include iostream using namespace std; template typename T // 声明模板和类型参数T T maxValue(T a, T b) { // 使用T作为参数和返回值类型 return (a b) ? a : b; } int main() { cout maxValue(3, 7) endl; // 调用maxValueint输出7 cout maxValue(3.5, 2.1) endl; // 调用maxValuedouble输出3.5 cout maxValue(a, z) endl; // 调用maxValuechar输出z return 0; }编译器会根据调用时传入的实参类型自动推导并生成对应类型的函数实例这个过程称为模板实例化。2. 类模板类模板允许定义一种通用的类其成员变量或成员函数的类型可以参数化。其基本语法如下template typename T // 或 template class T class ClassName { // 类成员声明和定义可以使用类型T };示例一个简单的栈Stack类模板#include iostream using namespace std; template typename T, int MAX_SIZE 100 // 可以包含非类型参数如int MAX_SIZE class Stack { private: T elements[MAX_SIZE]; // 使用类型参数T定义数组 int topIndex; public: Stack() : topIndex(-1) {} void push(const T value) { if (topIndex MAX_SIZE - 1) { elements[topIndex] value; } } T pop() { if (topIndex 0) { return elements[topIndex--]; } // 简化处理实际应处理异常 return T(); } bool isEmpty() const { return topIndex -1; } }; int main() { Stackint intStack; // 实例化一个存储int的栈使用默认大小100 intStack.push(10); intStack.push(20); cout intStack.pop() endl; // 输出20 Stackdouble, 50 doubleStack; // 实例化一个存储double的栈大小为50 doubleStack.push(3.14); return 0; }在实例化类模板时必须在类名后使用尖括号指定具体的模板参数如Stackint这与函数模板的自动推导不同。3. 模板的高级特性与用法3.1 模板特化与偏特化当通用模板无法满足特定类型的特殊需求时可以对其进行特化。全特化为所有模板参数提供具体的类型。// 通用模板 template typename T class MyClass { /*...*/ }; // 全特化版本针对T为int的情况 template class MyClassint { /*...*/ };偏特化局部特化仅对部分模板参数提供具体类型或进行限制。// 通用模板 template typename T1, typename T2 class MyPair { /*...*/ }; // 偏特化版本当两个类型相同时 template typename T class MyPairT, T { /*...*/ };3.2 模板与继承类模板可以参与继承关系既可以从普通类继承也可以从另一个类模板继承。// 基类模板 template typename T class Base { protected: T data; public: Base(T d) : data(d) {} }; // 派生类模板 template typename T class Derived : public BaseT { // 继承自BaseT使用相同的类型参数 public: Derived(T d) : BaseT(d) {} void print() { cout this-data endl; } // 注意访问基类成员可能需要使用this- };3.3 模板别名using 与 typedef为了简化复杂模板类型的书写可以使用typedef或 C11 引入的using来创建类型别名。template typename T using Vec std::vectorT; // 模板别名 Vecint intVec; // 等价于 std::vectorintusing在定义模板别名时比typedef更灵活直观。3.4 C20 概念与约束Concepts and RequiresC20 引入了概念Concepts来对模板参数施加约束使用requires关键字指定类型必须满足的条件这能产生更清晰的编译错误信息并增强代码可读性。#include concepts // 定义一个概念要求类型T可相加且结果类型可转换为T templatetypename T concept Addable requires(T a, T b) { { a b } - std::convertible_toT; }; // 使用概念约束函数模板 template Addable T T add(T a, T b) { return a b; } // 或者直接在模板声明中使用requires子句 template typename T requires AddableT T subtract(T a, T b) { return a - b; } // 此函数要求T满足Addable概念requires子句可以检查类型是否拥有特定成员、支持特定操作等。3.5 模板与友元模板类可以声明友元。友元声明可以针对特定类型也可以是模板形式。template typename U class OtherClass; // 前向声明 template typename T class MyClass { private: T secret; public: // 声明OtherClassint是MyClassT的友元特定实例友元 friend class OtherClassint; // 声明所有OtherClassU都是MyClassT的友元模板友元 template typename U friend class OtherClass; };4. 使用模板的注意事项编译期实例化模板代码在编译时根据使用情况进行实例化。如果未使用某个特定类型的模板则不会生成该版本的代码。定义与声明模板的定义实现通常需要放在头文件中因为编译器需要在实例化时看到完整的定义。代码膨胀过度使用模板可能导致为不同类型生成多份代码增加最终二进制文件的大小。调试难度模板相关的编译错误信息可能非常冗长和晦涩难懂尤其是在深度嵌套或涉及元编程时。分离编译问题将模板的声明与实现分离到.h和.cpp文件可能会导致链接错误。常见的做法是使用.hpp或.tpp文件来存放模板的实现。通过函数模板和类模板C实现了强大的泛型编程能力。结合模板特化、继承、别名、C20概念等高级特性可以构建出高度灵活、可复用且类型安全的抽象数据结构和算法库。参考来源C 中typedef的用法总结【C】requires的用法示例详解20 C 模板-类模板继承中类模板的使用 vs2019c 模板【C】友元接口与模板接口

更多文章