当前位置: 首页 > backend >正文

CPP模板编程

C++模版编程

C++ 模板编程是 C++ 语言的一个强大特性,通过编写泛型代码,可以创建能够处理多种数据类型的函数或类,而不需要为每种类型重复编写相同的代码逻辑。

一、为什么使用模板

比如我们现在想要完成这样一个工作,比较两个的大小。在C++中,我们可以通过函数重载的方式来完成这个工作。

// 函数重载
int compare(int x, int y) {cout << "int compare(int x, int y)" << endl;if (x > y) return 1;else if (x < y) return -1;else return 0;
}
int compare(double x, double y) {cout << "int compare(double x, double y)" << endl;if (x > y) return 1;else if (x < y) return -1;else return 0;
}

这两个函数除了参数列表不同,其余的功能等都是一样的,根据比较的值类型不同而需要写多个函数,造成代码冗余。C++中可以通过模板编程来简化开发。

二、模板的概念

模板(Templates)允许编写通用的代码,这些代码可以适应不同的数据类型,而无需为每种数据类型重复编写相同的逻辑。

模板是C++中泛型编程的基础, 作为强类型语言,C++ 要求所有变量都具有具体类型,由程序员显式声明或编译器推导。

int  a = 100; //必须明确声明变量a的数据类型

而模板允许程序员在定义类或函数时,编写与数据类型无关的通用代码。

比如:

template<class T> T compare(T x, T y);

或者

template<typename T> T compare(T x, T y);

区别

  • 模板参数列表中(template<...> 里),classtypename 完全等价,都是“类型参数”的意思。

  • 模板内部,如果要声明依赖于模板参数的类型(比如 T::value_type),必须用 typename

    template<typename T>
    void func() {typename T::value_type v; // 这里必须用 typename
    }
    

上面的代码描述了一个具有 T类型 的泛型函数的模板,其返回值和参数(x和 y)都具有此类型。那么T到底是什么类型?实际上,T只是一个占位符,具体是什么类型,此时并不知道,需要在具体调用的时候,指定参数的实际类型。

如下代码:此时T就是int类型。

int a = 10;
int b = 20;
int c = compare(a, b);

如下代码,此时T就是double类型。

double a = 5;
double b = 8;
double c = compare(a, b);

因此,对于T compare(T x, T y)来说,它就是一类函数的万能公式。具有通用性,但是不能直接使用。就好像我们编写简历时用的模板一样,不能直接用,在具体编写简历的时候,指定其中的内容。

模板可以分为函数模板和类模板两大类。

三、函数模版

C++中的函数模板是一种编写通用函数的机制,这些函数能够操作多种数据类型,而无需为每种类型单独编写函数定义。

1、函数模版的定义

语法形式如下:多个参数用逗号分隔。

template <typename t1,typename t2,...... > 返回值类型  函数名(参数列表){ //函数体 
}
template <class t1,class t2,......> 返回值类型  函数名(参数列表){ //函数体
}
  • template关键字表示这是一个模板定义。
  • typename T声明了一个类型参数T,表示函数可以接受任何类型作为此参数。
  • 模板定义中 <> 里的内容即模板参数列表,不能为空

示例:

template <typename T> T minmum(T t1, T t2)
{return t1 < t2 ? t1 : t2;
}
2、函数模板的使用

T的类型会根据实际调用minmum函数时其实参类型进行推导。

int main()
{int a = 100;int b = 200;int result = minmum(a, b);cout << result << endl;  double num1 = 1;double num2 = 2;double min = minmum(num1, num2);cout << min << endl;
}

函数模板本身不是一个函数,函数模板像一个公式,它列好了函数返回值类型、 函数名、函数的参数列表的参数类型,编译器通过这个公式在函数被调用时自动推导参数的类型,然后把这个模板实例化成相应的函数。

3、模板的实例化

函数模板的泛型参数类型的实际类型是在编译期间确定的,这个过程被称为模板实例化。

当编译器遇到一个函数模板的调用时,它会根据传给模板函数的参数类型来推导出模板参数的具体类型,然后生成针对这些具体类型的函数代码。

以上面函数为例,当用double类型使用函数模板时,编译器通过对实参类型的推导,将T确定为double类型,然后产生一份专门处理double类型的代码。这个过程叫实例化函数模板

在这里插入图片描述

4、隐式推导和显式推导

函数模板实例化过程中,其模板参数列表推导过程分为:隐式推导显式推导

(1)显示推导

明确指定参数类型。

int a = 100;
int b = 200;
int result = minmum<int>(a, b);

(2)隐式推导

隐式推导也叫自动类型推导

int a = 100;
int b = 200;
int result = minmum(a, b);

注意:隐式推导,不可以进行隐式类型转换。如下代码编译错误,char c不能隐式转换为int

int a = 100;
//int b = 200;
char c = 'A';
int result = minmum(a, c);
cout << result << endl;

1️⃣ 模板类型推导阶段

  • 当你写 template<typename T> T minmum(T x, T y);
    调用 minmum(a, c)
    • 编译器会尝试 推导 T
    • aint → 推导 T = int
    • cchar → 推导 T = char
    • 冲突 → 编译错误。
  • 重点:模板推导阶段不会做类型提升(char → int)或其他隐式转换。
    所以说“隐式推导,不可以进行隐式类型转换”。

2️⃣ 普通函数调用

  • 如果是普通函数:
int minmum(int x, int y);
minmum(a, c);
  • 这里会允许 隐式类型转换
    • char c 会自动提升为 int
    • 编译成功,返回结果也是 int

✅ 总结:

  • 模板函数:类型推导阶段禁止隐式类型转换,不一致就报错。
  • 普通函数:调用参数类型可以做隐式转换,符合函数参数类型即可。

模板类型推导阶段不会做隐式转换,但显式指定模板参数后,就不是推导阶段,而是普通函数调用阶段,允许隐式转换。如:

double m = 3.5, n = 7.2;
cout << max<int>(m, n) << endl;
m 和 n 是 double
模板要求参数类型为 T,这里就是 int → T a, T b 实际就是 int a, int b
这里发生了普通的隐式类型转换:
double m → 转换成 int(小数部分丢失)
double n → 转换成 int

(3)错误案例

#include <iostream>
template <typename T,typename E> T maxNum(T t1,T t2) {return t1 > t2 ? t1 : t2;
}
int main(){int result =minNum<int>(20, 30); //会报错,因为上面定义E类型,但是使用时没有指定E的类型//解决方案:int result =minNum<int,int>(20, 30); 或者在template模板中把E类型去掉return 0;
}

四、类模板

类模板是面向类型的通用蓝图,让同一个类逻辑可以支持多种数据类型,提高复用性、类型安全性和开发效率。

  • 与函数模板不同,编译器不能为类模板推断模板的参数类型,需要显示指定参数类型;同时类模板参数可以有默认值。
  • 将类模板和函数模板的定义写在头文件中,通常不需要为它们单独创建对应的.cpp文件。这是因为模板的实例化发生在使用模板的地方,编译器需要看到模板的完整定义才能生成函数或类的代码。如果将模板的定义放在.cpp文件中,编译器在处理那些包含模板声明的 .h 文件时无法看到具体的实现,从而无法为每一个具体类型生成对应的模板实例。
1、语法
template <typename T1, typename T2,...,typename Tn> class 类模板名
{
// 类内成员定义
};
template <class T1, class T2,...,class Tn> class 类模板名
{
// 类内成员定义
};
2、类模板的定义及使用
template <class T>    // 或 template<typename T>
class MyTemplateClass 
{
public:MyTemplateClass(T data): data(data) {}void print() {std::cout << data << std::endl;}
private:T data;
};
  • T 就是模板参数,代表类中某个成员的数据类型。
  • 可以有多个模板参数:template <typename T1, typename T2>
  • 可以指定默认类型:template <typename T=int>
#include "MyTemplateClass.h"int main() {MyTemplateClass<int> obj1(100);      // T=intMyTemplateClass<double> obj2(3.14);  // T=doubleMyTemplateClass<std::string> obj3("Hello"); // T=std::stringobj1.print();  // 输出:100obj2.print();  // 输出:3.14obj3.print();  // 输出:Helloreturn 0;
}
  • 使用时 必须显示指定类型(与函数模板不同,函数模板可以让编译器推导类型)。
  • 编译器在看到 MyTemplateClass<int> 时,会根据模板生成一个具体的 int 类,生成 MyTemplateClass<double> 时又生成一个对应的 double 类。

类模板(Class Template)就是 用类型参数化的类,相当于给类开了一个“通用接口”,允许用不同的数据类型生成对应的类。

  • 核心思想:把类的数据类型抽象成模板参数,使用时再指定具体类型。
  • 优点
    1. 复用性强:同一个模板可以生成多种类型的类,而不需要重复写代码。
    2. 类型安全:编译器在实例化时会检查类型。
    3. 减少冗余:避免手动为每种类型写一份几乎一样的类。
http://www.xdnf.cn/news/17791.html

相关文章:

  • Python学习-----3.基础语法(2)
  • 广义矩估计随机近似中1.2和2.1的差异
  • 如何手动开启 Hyper-V?Windows 10/11 详细开启教程
  • Mybatis 源码解读-Plugin插件源码
  • 系统设计——DDD领域模型驱动实践
  • 如何写出更清晰易读的布尔逻辑判断?
  • 码上爬第九题【协程+webpack】
  • rustdesk 开源遥控软件
  • Wireshark中捕获的大量UDP数据
  • C# 结构体与类的区别是什么?
  • 【论文阅读 | CVPR 2024 | UniRGB-IR:通过适配器调优实现可见光-红外语义任务的统一框架】
  • C++ 23种设计模式的分类总结
  • 软件著作权产生与登记关键点
  • PiscTrace基于YOLO追踪算法的物体速度检测系统详解
  • openvsx搭建私有插件仓库
  • mysql查询中的filesort是指什么
  • 云蝠智能 VoiceAgent:重构物流售后场景的智能化引擎
  • 构建Node.js单可执行应用(SEA)的方法
  • 飞算JavaAI合并项目实战:7天完成3年遗留系统重构
  • BitDock——让你的Windows桌面变为Mac
  • 网络层协议——IP
  • 前端Vite介绍(现代化前端构建工具,由尤雨溪开发,旨在显著提升开发体验和构建效率)ES模块(ESM)、与传统Webpack对比、Rollup打包
  • 设计模式(1)
  • vue2和vue3的区别
  • httpx 设置速率控制 limit 时需要注意 timeout 包含 pool 中等待时间
  • 【2025年 Arxiv 即插即用】 特征融合新突破:空间–光谱注意力融合模块 SAFFM 强势登场!
  • Vite 为什么比 Webpack 快?原理深度分析
  • 【Linux系统】进程的生命旅程:从创建到独立的演绎
  • RTC时钟倒计时数码管同步显示实现(STC8)
  • 如何安装 scikit-learn Python 库