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

详解 C++中的模板

目录

前言

一、函数模板

1.定义

2.函数模板的实现

3.模板函数的实例化

4.模板参数的省略

1.函数模板的实参推导

2.类模板的实参推导

3.默认模板参数

4.特殊情况:无法推导的模板

5.推导失败的情况

二、类模板

1.概念和定义

2.类模板定义

3.类模板的使用

4.类模板的定义格式


前言

        这篇文章主要介绍C++中的模板。

一、函数模板

1.定义

        模板是一系列相关函数的模型或样板,这些函数的源代码形式相同,只是所针对的数据类型不同。

        在开发过程中,我们经常遇到这种情况,有两个或者两个以上的函数,其功能是相同的,仅仅是数据类型的不同。例如:

int add(int a,int b){return a + b;
}
int add(float a,float b){return a + b;
}double add(double a,double b){return a + b;
}

        上面的三个函数都实现了对数据的加法运算,唯一不同的是参数类型不同。这样的函数可以采用函数模板实现简便化。

2.函数模板的实现

        函数模板的格式如下:

template <<模板形参声明>> <函数声明>

        模板形参是由一个或者多个<模板形参>组成的,如果是多个需要用逗号隔开。每个模板具有以下几种形式:

1.typename <参数名>

2.class <参数明>

3.<类型修饰><参数名>

        对于上述实例中的 add 函数,我们可以如下定义:

template <typename T>
T add(T a, T b) {return a + b;
}

3.模板函数的实例化

        在实际开发过程中,我们使用实参的实际类型代替虚拟类型即可。

#include <iostream>
#include <iomanip> // 需要包含这个头文件来设置精度
using namespace std;template <typename T>
T add(T a, T b) {return a + b;
}int main() {int a = 10, b = 20;cout << "a + b = " << add(a, b) << endl;// 使用浮点数并设置精度double c = 10.0, d = 20.0;cout << fixed << setprecision(2);  // 设置保留两位小数cout << "c + d = " << add(c, d) << endl;double e = 10.00, f = 20.00;cout << "e + f = " << add(e, f) << endl;return 0;
}

4.模板参数的省略

        在C++中,模板实参可以在某些情况下进行省略,称为 模板实参推导。这一特性允许编译器根据传递给函数或类的参数自动推导出模板的类型。以下是几种常见的模板实参省略场景:        

1.函数模板的实参推导

        对于函数模板,编译器可以根据调用时传递的函数参数来推导模板实参。例如:

template <typename T>
T add(T a, T b) {return a + b;
}int main() {int x = 5, y = 10;// 不需要显式指定类型,编译器会推导T为intcout << add(x, y) << endl;
}

        在上述例子中,add(x, y) 调用时,编译器会根据 x 和 y 的类型(int)自动推导 T 的类型为 int。

2.类模板的实参推导

        在C++17之前,类模板的实参必须显式指定,但从C++17开始,可以省略某些类模板的实参。编译器会从构造函数参数中推导出模板实参。例如:

template <typename T>
T add(T a, T b) {return a + b;
}int main() {int x = 5, y = 10;// 不需要显式指定类型,编译器会推导T为intcout << add(x, y) << endl;
}

        在这个例子中,Box box(123); 中并没有显式地指定 Box<int>,编译器根据传递的值 123 推导出 T 的类型为 int。

3.默认模板参数

        你可以为模板提供默认的模板参数,这样在不提供实参时会使用默认值。例如:

template <typename T = int>
T multiply(T a, T b) {return a * b;
}int main() {cout << multiply(3, 4) << endl; // T被推导为int,因为int是默认类型cout << multiply<double>(3.5, 2.5) << endl; // T显式为double
}

        在这个例子中,multiply(3, 4) 直接使用了默认的模板参数 T = int,而 multiply<double> 明确指定了模板参数。

4.特殊情况:无法推导的模板

        并非所有情况下模板都可以自动推导,特别是当模板类型不直接与参数关联时。例如,某些模板的类型依赖于非参数部分时,编译器无法自动推导,这时需要显式指定模板实参。

template <typename T, typename U>
void printPair(T a, U b) {cout << a << " and " << b << endl;
}int main() {printPair(1, "hello"); // 编译器能够推导T为int,U为const char*printPair<int>("test", 100); // 必须显式指定其中一个模板参数
}

5.推导失败的情况

        有时候模板的推导会失败,特别是类型不匹配或涉及复杂的类型转换时。此时需要显式指定模板实参来避免推导失败。

二、类模板

1.概念和定义

        类模板是通过引入模板参数来定义类的。在类模板的声明中,使用 template 关键字标识模板类型参数,然后在类的定义中可以使用这些模板参数,就像使用普通的数据类型一样。

2.类模板定义

        一个简单的类模板可以按如下方式定义:

template <typename T>
class Box {
private:T value;
public:Box(T val) : value(val) {}T getValue() {return value;}
};

        在上述代码中,T 是一个模板参数,可以被替换为任何具体的数据类型(例如 int、double 或 string)。当我们使用这个类时,必须在实例化类时提供一个具体的数据类型。

3.类模板的使用

        在这个例子中,Box<int> 和 Box<double> 分别创建了 int 和 double 类型的对象,并且它们可以分别存储 int 和 double 类型的数据。        ​​​​​​​        

int main() {Box<int> intBox(100);  // 使用int类型Box<double> doubleBox(100.5);  // 使用double类型std::cout << "intBox value: " << intBox.getValue() << std::endl;std::cout << "doubleBox value: " << doubleBox.getValue() << std::endl;return 0;
}

4.类模板的定义格式

        类模板的定义格式如下:

template <typename T> 
class ClassName {// 成员变量和方法使用模板参数T
};

        类模板的优势在于能够通过一种通用方式处理不同类型的数据,使代码更具通用性和灵活性,适用于需要同样逻辑但适用于不同数据类型的情况。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • JAVA基本简介(期末)
  • MongoDB解说
  • 9.24工作笔记
  • Spark 任务与 Spark Streaming 任务的差异详解
  • 9.创新与未来:ChatGPT的新功能和趋势【9/10】
  • fastadmin 根据选择数据来传参给selectpage输入框
  • 【算法】模拟:(leetcode)6.Z 字形变换(medium)
  • Java提供了一个跨平台的换行符\n
  • YOLOv5物体检测
  • 8086的指令系统
  • 力扣 24.两两交换链表中的节点
  • 华为杯”第十二届中国研究生数学建模竞赛-B题: 数据的多流形结构分析(续)
  • 云岚到家 第一天实战总结
  • 云栖实录 | 阿里云 OpenLake 解决方案重磅发布:多模态数据统一纳管、引擎平权联合计算、数据共享统一读写
  • 高等数学的后续课程
  • 【347天】每日项目总结系列085(2018.01.18)
  • JavaScript 基本功--面试宝典
  • JavaScript/HTML5图表开发工具JavaScript Charts v3.19.6发布【附下载】
  • Java反射-动态类加载和重新加载
  • java小心机(3)| 浅析finalize()
  • Js实现点击查看全文(类似今日头条、知乎日报效果)
  • Linux Process Manage
  • node和express搭建代理服务器(源码)
  • PHP 程序员也能做的 Java 开发 30分钟使用 netty 轻松打造一个高性能 websocket 服务...
  • SAP云平台里Global Account和Sub Account的关系
  • TiDB 源码阅读系列文章(十)Chunk 和执行框架简介
  • 事件委托的小应用
  • 一起来学SpringBoot | 第十篇:使用Spring Cache集成Redis
  • 在electron中实现跨域请求,无需更改服务器端设置
  • 中国人寿如何基于容器搭建金融PaaS云平台
  • Spring第一个helloWorld
  • ​猴子吃桃问题:每天都吃了前一天剩下的一半多一个。
  • $ is not function   和JQUERY 命名 冲突的解说 Jquer问题 (
  • $.ajax()
  • (4)事件处理——(6)给.ready()回调函数传递一个参数(Passing an argument to the .ready() callback)...
  • (c语言+数据结构链表)项目:贪吃蛇
  • (二刷)代码随想录第15天|层序遍历 226.翻转二叉树 101.对称二叉树2
  • (附源码)spring boot车辆管理系统 毕业设计 031034
  • (附源码)SSM环卫人员管理平台 计算机毕设36412
  • (剑指Offer)面试题41:和为s的连续正数序列
  • (六) ES6 新特性 —— 迭代器(iterator)
  • (新)网络工程师考点串讲与真题详解
  • (学习日记)2024.01.19
  • (转)我也是一只IT小小鸟
  • (转载)VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)
  • .babyk勒索病毒解析:恶意更新如何威胁您的数据安全
  • .gitignore文件—git忽略文件
  • .NET Core 中的路径问题
  • .net web项目 调用webService
  • .net 程序发生了一个不可捕获的异常
  • .Net 垃圾回收机制原理(二)
  • .net 生成二级域名
  • .net 验证控件和javaScript的冲突问题
  • .NETCORE 开发登录接口MFA谷歌多因子身份验证
  • .net操作Excel出错解决