【C++】模板详解

前言:在之前的学习我们发现我们无时无刻都用到模板这个东西,但是博主一直没有进行讲解,今天我们就一次性对模板进行一个整体的学习与讲解。

💖 博主CSDN主页:卫卫卫的个人主页 💞
👉 专栏分类:高质量C++学习 👈
💯代码仓库:卫卫周大胖的学习日记💫
💪关注博主和博主一起学习!一起努力!
在这里插入图片描述


目录标题

  • 什么是模板
    • 函数模板
    • 类模板
    • 类模板的特化
    • 非类型模板参数


什么是模板

模板是C++中的一种编程工具,用于在编译时实现泛型编程。它允许程序员编写与特定数据类型无关的代码,以便在不同的情况下重用和适应不同的数据类型。

模板可以用来定义函数模板和类模板。函数模板允许在不同的数据类型上重复使用同一段代码,而类模板允许在需要时使用不同的数据类型实例化。

使用模板的好处是可以提高代码的重用性和灵活性。它允许程序员编写通用的算法和数据结构,可以适应不同类型的数据,而无需为每种类型编写不同的代码。

下面我们可以看一个例子:

void Swap(int& left, int& right)
{int temp = left;left = right;right = temp;
}
void Swap(double& left, double& right)
{double temp = left;left = right;right = temp;
}
void Swap(char& left, char& right)
{char temp = left;left = right;right = temp;
}
...
...
...

在上面例子中我们不难发现,当我们想写一个进行交换的代码的时候,仅仅因为传过来的数据类型,我们就要写一堆重复的代码,因此模板的作用就充分的体现出来了。模板使用特定的语法来定义和使用。例如,函数模板的定义使用关键字"template"和模板参数来表示通用的数据类型。类模板的定义使用类似的语法,将模板参数放在类名后面的尖括号中。

使用模板时,程序员可以在编译时指定模板参数的具体类型,从而生成特定的代码实例。这个过程被称为模板实例化。

总之,模板是C++中一种强大的机制,允许程序员编写通用的代码,可以适应不同的数据类型。通过使用模板,可以提高代码的重用性和灵活性,使程序更加可维护和可扩展。


函数模板

(1)什么是函数模板
在C++中,函数模板可以用来定义通用的函数,可以根据不同的数据类型自动生成相应的函数代码。
函数模板的定义方法

template <typename T>
返回类型 函数名(参数列表) {// 函数体
}

其中,typename T为模板参数,可以是任何合法的类型,返回类型为函数的返回类型,函数名为函数的名称,参数列表为函数的参数列表。

下面是一个函数模板的示例,用于交换两个变量的值(基本解决了上面代码重复的问题):

template <typename T>
void swap(T& left, T& right)
//这里的T代表的模板的类型名,可以通过传过来的数据识别数据类型,编译器会自动转成相应的类型 
{T temp = left;left = right;right = temp;
}

说明:

  1. template是声明模板的关键字,告诉编译器开始泛型编程。
  2. 尖括号<>中的typename是定义形参的关键字,用来说明其后的形参名为类型 参数,(模板形参)。Typename(建议用)可以用class关键字代替,两者没有区别。
  3. 模板形参(类属参数)不能为空(俗成约定用一个大写英文字母表示),且在函 数定义部分的参数列表中至少出现一次。与函数形参类似,可以用在函数定义的各 个位置:返回值、形参列表和函数体。
  4. 函数定义部分:与普通函数定义方式相同,只是参数列表中的数据类型要使用 尖括,号<>中的模板形参名来说明。当然也可以使用一般的类型参数

(2)函数模板的隐式类型转化
根据函数调用时传入的数据类型,推演出模板形参。因此

template <typename T>
void my_swap(T& left, T& right) {T temp = left;left = right;right = temp;
}
int main()
{int a = 3, b = 5;my_swap(a, b);//根据调用的类型会自动转变cout << a << " " << b << endl;double a1 = 3.4, b1 = 5.3;my_swap(a1, b1);cout << a1 << " " << b1 << endl;return 0;
}

(3)函数模板的实例化
C++中的函数模板实例化是指在调用函数模板时,根据具体的参数类型生成对应的函数实例的过程。函数模板的实例化可以通过两种方式来实现:显式实例化和隐式实例化。

  1. 显式实例化:在使用函数模板之前,使用template关键字加上具体的参数类型来显式地实例化函数模板。例如:
template <typename T>
void foo(T value) {// 函数模板的定义
}// 显式实例化函数模板
template void foo<int>(int);

这样就会生成一个具体的函数实例,用于处理整型参数。

  1. 隐式实例化:当调用函数模板时,编译器会根据传入的实参类型自动推导出所需的函数实例。例如:
template <typename T>
void foo(T value) {// 函数模板的定义
}int main() {// 隐式实例化函数模板foo(42);  // 参数为整型,编译器会自动推导出foo<int>(int)foo(3.14);  // 参数为浮点型,编译器会自动推导出foo<double>(double)
}

在调用foo(42)foo(3.14)时,编译器会根据参数类型自动实例化函数模板,并生成对应的函数实例。

需要注意的是,编译器只会根据需要实例化的函数模板参数类型生成对应的函数实例,而不会为所有可能的函数模板参数类型都生成函数实例,这样可以避免生成过多的代码和增加编译时间。


(4)函数模板的重载
函数模板的重载是指在同一个作用域内定义多个函数模板,它们具有相同的函数名但参数列表不同,以实现对不同类型的参数进行不同的处理。

函数模板的重载可以有两种形式:非模板函数重载和函数模板特化重载。

  1. 非模板函数重载:在函数模板之外定义一个与函数模板同名但参数列表不同的非模板函数。例如
template <typename T>
void foo(T value) {// 函数模板的定义
}void foo(int value) {// 非模板函数的定义
}int main() {foo(42);  // 调用非模板函数foo(int)foo(3.14);  // 调用函数模板foo<T>(T)
}

注意:当函数模板和普通函数都符合调用时,优先选择普通函数。

(5)函数模板的特化

函数模板特化是指针对特定类型的参数,定义一个特定的函数模板版本。可以为某个具体类型的参数定义一个特定的函数模板,以提供更具体的实现或逻辑(通俗的理解为一个特定的类型指定一种处理方式)。

template <typename T>
void print(T value) {std::cout << "Generic template function: " << value << std::endl;
}template <>
void print<int>(int value)//对于int类型进行特化处理 
{std::cout << "Specialized template function for int: " << value << std::endl;
}int main() {print(5.5);      // 调用通用模板函数print(10);       // 调用特化的模板函数print("Hello");  // 调用通用模板函数return 0;
}

输出结果:

Generic template function: 5.5
Specialized template function for int: 10
Generic template function: Hello

在上述示例中,我们首先定义了一个名为print的函数模板,用于打印不同类型的参数。然后我们对print函数模板进行了特化,使其具有对int类型参数的特定实现。在main函数中,我们分别调用了print函数模板,并输出了相应的结果。我们可以看到,对于int类型的参数,调用的是特化的版本,而对于其他类型的参数,调用的是通用版本。


类模板

C++中的类模板(class template)是一种用来定义通用类的工具。类模板可以根据参数化的类型生成不同的类。通过类模板,我们可以在不同的类型上实例化一个类,从而实现代码的复用和泛化(类模板的定义方式和函数模板大同小异)。

template <typename T>
class ClassName {// 类成员和成员函数的定义
};

上面的代码中,template 表示这是一个类模板,T是一个类型参数。在类中,我们可以使用T作为类型来定义成员变量、成员函数等。

在使用类模板时,需要在实例化时指定类型的具体值。可以通过在类名后加上的方式来实例化一个类模板,其中Type是具体的类型。

下面是一个使用类模板的例子:

template <typename T>//类模板
class Pair {
private:T _first;T _second;public:Pair(T first, T second) {_first = first;_second = second;}T getFirst() {return _first;}T getSecond() {return _second;}
};int main() {Pair<int> pair(1, 2);//类模板的实例化cout << pair.getFirst() << " " << pair.getSecond() << endl;Pair<string> pair2("Hello", "World");cout << pair2.getFirst() << " " << pair2.getSecond() << endl;return 0;
}

在上面的例子中,Pair是一个类模板,可以根据参数化的类型来生成不同的类。在main函数中,我们分别使用int和string作为类型参数来实例化了Pair类,并通过调用getFirst和getSecond函数来获取成员变量的值。

注: 与函数模板不同的是,类模板在实例化时,必须在尖括号中为模板形参显式 地指明数据类型(实参),编译器不能根据给定的数据推演出数据类型。即:不存 在将整型值10推演为int类型传递给模板形参的实参推演过程,必须要在<>中指 定int类型。


类模板的特化

在C++中,类模板可以通过特化来更具体地定义模板参数的类型。类模板特化有两种方式:全特化和偏特化。

全特化:当模板参数的具体类型已知时,可以使用全特化来定义类模板的特定实现。全特化使用特定的类型替换模板参数,并提供对应的实现代码(和前面函数模板的特化类似,对指定的类型进行特殊处理)。

全特化的语法如下:

template<typename T>
class MyTemplate { // 模板的通用实现
};template<>
class MyTemplate<int> {// 对于T为int类型的特化实现
};template<>
class MyTemplate<double> {// 对于T为double类型的特化实现
};

类模板的偏特化是指对类模板的部分模板参数进行特化,而保持其他模板参数为通用实现的一种技术。偏特化允许根据特定的条件或类型,定义不同的特化实现。

偏特化有两种形式:主模板偏特化和部分特化

  1. 主模板偏特化:主模板偏特化是通过指定一个或多个特定的类型来对模板参数进行特化,保持其他参数为通用实现。主模板偏特化的语法如下:
template<typename T, typename U>
class MyTemplate {// 主模板的通用实现
};template<typename U>
class MyTemplate<int, U>//相当于当T为int类型时,对该模板进行了偏特化 
{// 对T为int类型的特化实现
};

在上述示例中,我们对MyTemplate的第一个模板参数T进行了特化,当Tint时,使用特化实现。

  1. 部分特化:部分特化是通过指定特定的条件来对模板参数进行特化,保持其他参数为通用实现。部分特化的语法如下:
template<typename T, typename U>
class MyTemplate {// 主模板的通用实现
};template<typename T>
class MyTemplate<T, int> {// 对U为int类型的特化实现
};

在上述示例中,我们对MyTemplate的第二个模板参数U进行了特化,当Uint时,使用特化实现。

使用类模板的偏特化时,需要注意以下几点:

  • 偏特化的实现必须在主模板定义之后。
  • 一个模板只能有一个主模板(通用实现),但可以有多个偏特化版本,每个偏特化版本针对不同的条件进行特化。
  • 在使用类模板时,编译器会自动选择与提供的模板参数最匹配的特化版本。

通过偏特化,我们可以根据特定的类型或条件,对类模板进行更具体的实现,以满足特殊的需求或提高性能。


非类型模板参数

C++中的非类型模板参数是指在模板定义中可以使用的常量表达式作为参数的一种方式。这些参数可以是整数、枚举类型、指针、引用等,但不能是浮点数、类类型或成员指针。

非类型模板参数可以在编译时提供,它们的值在编译期间是确定的,并且在生成代码时被替换为实际的值。这种编译时常量化的机制可以用于优化代码和提供更灵活的模板实例化。

非类型模板参数的语法如下:

template <typename T, int N>
class MyClass {// 类模板定义
};

上述示例中,N是一个非类型模板参数,它的类型是int。在实例化MyClass时,必须通过一个常量表达式来提供一个整数值作为模板参数。

使用非类型模板参数时,需要注意以下几点:

  • 非类型模板参数必须是一个常量表达式,可以在编译时求值。
  • 非类型模板参数必须在编译时已知,不能在运行时才确定。
  • 非类型模板参数可以用于模板参数的类型推导,编译器可以自动推导模板参数的类型,无需显式指定。
  • 非类型模板参数可以用于数组长度、类型选择、编译时分支等,提供了更灵活的模板实例化方式。

使用非类型模板参数: 可以在类模板的定义中使用非类型模板参数,如下所示:

template <typename T, int N>
class MyClass {
public:void foo() {std::cout << N << std::endl;}
};

在上述示例中,foo函数输出了非类型模板参数N的值。

通过使用非类型模板参数,可以在编译期间根据常量表达式的值来生成特定的模板实例化,从而提供更灵活和优化的代码。


好啦,今天的内容就到这里啦,下期内容预告C++中的继承.


结语:后面就要进入C++进阶的内容了,难度又慢慢提升了大家一起努力!。


🌏🗺️ 这里祝各位接下来的每一天好运连连 💞💞

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/34578.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机毕业设计Python+LSTM+Tensorflow股票分析预测 基金分析预测 股票爬虫 大数据毕业设计 深度学习 机器学习 数据可视化 人工智能

基于TensorFlow-LSTM的股票预测系统开题报告 一、研究背景与意义 随着信息技术的飞速发展&#xff0c;股票市场作为现代经济活动的重要组成部分&#xff0c;其价格波动受到广泛关注。投资者们迫切希望通过科学的方法预测股票价格&#xff0c;以优化投资决策&#xff0c;实现利…

【Java06】Java中的类与对象

1. 类和对象 Java中的类模版如下&#xff1a; [修饰符] class 类名 {0~n个构造器;0~n个成员变量;0~n个成员方法; }构造器是类创建对象的根本途径。如果没有显式定义构造器&#xff0c;系统会默认提供一个。成员变量、成员方法的定义和C类似&#xff0c;只不过多了修饰符。 Ja…

前端基础——自学习梳理

超文本协议HTML <!DOCTYPE HTML> <html><head><meta charset"utf-8"> <style> /*Css*/.sty1{height:100px;width:100px;background-color: red;margin-top: 10px;float:left;margin-left: 10px;box-shadow: 10px 10px 10px #0000…

多路h265监控录放开发-(14)通过PaintCell自定义日历控件继承QCalendarWidget的XCalendar类

首先创建一个新类XCalendar继承QCalendarWidget类&#xff0c;然后在UI视图设计器中把日历提升为XCalendar&#xff0c;通过这个函数自己设置日历的样式 xcalendar.h #pragma once #include <QCalendarWidget> class XCalendar :public QCalendarWidget { public:XCal…

Linux 运维王者从不离手的10款工具

运维工程师在日常工作中频繁运用的10款工具&#xff0c;并细致阐述每款工具的功能、适用场景以及其卓越之处。 1. Shell脚本 功能&#xff1a;主要用于自动化任务和批处理作业。 适用场景&#xff1a;频繁用于文件处理、系统管理、简单的网络管理等操作。 优势&#xff1a;灵…

Java 抽象类

目录 1、什么是抽象类 2、定义抽象类 3、抽象类特性 4、 抽象类的作用 1、什么是抽象类 抽象类&#xff0c;顾名思义就是抽象的。该类没有包含足够的信息去描绘一个具体的对象&#xff0c;这样的类称为抽象类。抽象类着一种优化了的概念组织方式&#xff0c;它是所有子类的…

研究上百个小时,高手总结了这份 DALL-E 3 人物连续性公式(上)

上篇 Dall-E 3 讲了常见的 20 个公式&#xff0c;今天单独来讲一下人物连续性公式&#xff0c;这个公式来自 AshutoshShrivastava。 上篇回顾&#xff1a; 效果超好&#xff01;全新 DALL-E 3 必须掌握的 20 种公式使用方法上周末&#xff0c;DALL-E 3 正式加入 ChatGpt&…

嵌入式实验---实验八 ADC电压采集实验

一、实验目的 1、掌握STM32F103ADC电压采集程序设计流程&#xff1b; 2、熟悉STM32固件库的基本使用。 二、实验原理 1、使用STM32F103R6采集可变电阻上的电压信号&#xff0c;并通过计算把当前ADC转换值和电压值显示在LCD1602液晶屏上&#xff1b; 2、对照电压表读数&…

红队内网攻防渗透:内网渗透之内网对抗:横向移动篇域控系统提权NetLogonADCSPACKDC永恒之蓝CVE漏洞

红队内网攻防渗透 1. 内网横向移动1.1 横向移动-域控提权-CVE-2020-1472 NetLogon1.2 横向移动-域控提权-CVE-2021-422871.3 横向移动-域控提权-CVE-2022-269231.4 横向移动-系统漏洞-CVE-2017-01461.5 横向移动-域控提权-CVE-2014-63241. 内网横向移动 1、横向移动-域控提权-…

为冲刺IPO,喜马拉雅曝裁员20%?钉钉叶军吐槽百度搜索;美国制裁俄罗斯安全软件12名高管;华为自研语言仓颉力战Java

一、商业圈 1.钉钉总裁叶军吐槽百度搜索&#xff1a;前十条都是广告 钉钉总裁叶军在亚布力中国企业家论坛第十届创新年会上发表了演讲&#xff0c;期间他直言不讳地对百度搜索提出了批评。叶军指出&#xff0c;在OpenAI推出智能聊天机器人ChatGPT之后&#xff0c;百度的传统搜…

【Web APIs】JavaScript 事件基础 ② ( “ 事件 “ 开发步骤 | 常见鼠标 “ 事件 “ )

文章目录 一、" 事件 " 开发步骤1、" 事件 " 开发步骤2、完整代码示例 二、常见鼠标 " 事件 "1、常见鼠标 " 事件 "2、鼠标 " 事件 " 代码示例 Web APIs 博客相关参考文档 : WebAPIs 参考文档 : https://developer.mozilla…

6个步骤实现 Postman 接口压力测试

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 1、第一步接口可以通的情况下点击右上角save 2、将相应信息填入 3、如果是同一个接口修改不同的…

大型国民老牌药品医疗企业如何借助实时数仓冲破数据孤岛桎梏,拥抱数据驱动的经营管理模式

使用 TapData&#xff0c;化繁为简&#xff0c;摆脱手动搭建、维护数据管道的诸多烦扰&#xff0c;轻量代替 OGG、DSG 等同步工具&#xff0c;「CDC 流处理 数据集成」组合拳&#xff0c;加速仓内数据流转&#xff0c;帮助企业将真正具有业务价值的数据作用到实处&#xff0c…

Mysql: 数据模型

一.关系型数据库 概念:建立在关系型基础上,由多张相互连接的二维表组成的数据库。 1.关系型数据库: 2.特点&#xff1a; 1.使用表存储数据,格式统一,便于维护。 2.使用SQL语言操作,标准统一,使用方便。 3.数据模型 通过客户端连接DBMS可以创建多个数据库,在数据库中…

光伏半导体的种类

光照射半导体材料时&#xff0c;其电导率发生变化的实质是光生载流子的产生。在半导体中&#xff0c;价带中的电子受到一定能量的光子激发后&#xff0c;可以跃迁到导带&#xff0c;形成自由电子和空穴对&#xff0c;即光生载流子。这些光生载流子会增加半导体的导电能力&#…

ZOOM太卡怎么办?公司如何解决ZOOM会议卡顿?

ZOOM作为一种常见的办公工具&#xff0c;尤其在跨国公司和外资企业中&#xff0c;在线会议非常普遍。然而&#xff0c;由于ZOOM的服务器部署在国外&#xff0c;国内用户使用时可能会遇到卡顿、不稳定和声音断续等问题。那么&#xff0c;如何有效解决ZOOM卡顿的问题呢&#xff1…

2024国有企业数字化转型的意义和作用是什么?

一、当下国有企业数字化转型最新的意义及作用是什么? 数字化转型对国有企业具有深远的意义&#xff0c;不仅是企业的内在需求&#xff0c;更是国家经济发展的重要支撑。据研究表明&#xff0c;数字化相关技术可为企业提升约60%的作业效率&#xff0c;降低20%的人力成本&#…

项目训练营第五天

项目训练营第五天 后端代码优化 通用异常处理类编写 Data public class BaseResponse<T> implements Serializable {int code;T data;String message null;String description null;public BaseResponse(int code, T data, String message, String description) {th…

Qt画实时曲线图

Qt引入QcustomPlot 首先下载QcustomPlot源代码&#xff0c;https://github.com/qcustomplot/qcustomplot 下载zip文件 运行所下载的项目生成库文件libqcustomplotd2.a文件和qcustomplotd2.dll文件。 在项目中添加printsupport。 并将qcustomplot.h文件和qcustomplot.cpp文…

万物皆对象,你信吗?

**内存空间和数据都消失&#xff0c;数据怎么会消失的&#xff1f;**空间没了&#xff0c;数据自然也跟着消失。因为数据就是在空间里面的。就像宇宙大爆炸&#xff0c;我们还能存在嘛&#xff0c;是不是已经undefined了。「一块小内存上有2种数据类型」 内部存储的数据 地址值…