C++的进阶泛型编程学习(1):函数模板的基本概念和机制

news2024/11/26 2:27:08

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、模板
    • 1.1 模板的概念
      • 1.1.1 形象的解释:模板就是通用的模具,目的是提高通用性
      • 1.1.1 模板的特点:
      • 1.1.2 综述模板的作用
    • 1.2 模板的使用机制
      • 1.2.1 函数模板
  • 二、函数模板的深入学习及注意机制
    • 2.1 函数模板的自动类型推导
      • 2.1.1 ①自动类型推导,必须使得推导出的数据类型T是一致的
    • 2.2 typename为什么可以替换为class?
    • 2.3 函数模板的功能及使用时机
  • 三、函数模板具体案例——数组排序
    • 3.1 目的:测试char数组和int数组的排序
    • 3.2 思路:
    • 3.3 全部代码
  • 四、普通函数与函数模板的区别
    • (1)定义方式:
    • (2)参数类型:
    • (3)重载:
    • (4)代码生成:
    • (5)使用方式:
  • 五、普通函数和函数模板的调用规则
    • 5.1 如果函数模板和普通函数都可以实现,优先调用普通函数
    • 5.2 可以通过空模板参数列表来强制调用函数模板
    • 5.3 函数模板也可以发生重载
    • 5.4 如果函数模板可以产生更好的匹配优先调用函数模板
  • 六、函数模板的局限性:不是万能的
    • 6.1 问题提出:函数模板无法解决数组、结构体这类特殊传参的比较
    • 6.2 解决办法
  • 七、学习模板并不是为了写模板,而是在STL(标准模板库)能够运用系统提供的模板


前言

回顾一下C++的面向对象特性有哪些?
在这里插入图片描述
在这里插入图片描述
我用这两个表来简要的总结了一下,接下来要接触的,是C++中更加复杂,更加进阶的内容

一、模板

1.1 模板的概念

C++的模板是一种通用编程工具,它允许在编写代码时定义通用的数据类型或函数,以便在不同的上下文中重复使用。模板可以用于类、函数和类成员函数。

1.1.1 形象的解释:模板就是通用的模具,目的是提高通用性

在这里插入图片描述
另外比如说PPT模板、实验报告模板等等。

1.1.1 模板的特点:

①模板不可以直接使用,他只是一个框架
②模板的通用不是万能的,只在一个或者几个方面发挥作用
所以C++里面的模板的作用也是大同小异

1.1.2 综述模板的作用

C++中的模板是一种通用编程工具,它允许在编写代码时定义通用的数据类型或函数,以便在不同的上下文中重复使用。模板的作用包括代码重用、泛型编程、类型安全、高性能代码生成以及提供容器和算法库等功能。通过使用模板,可以编写通用的代码,适应不同的数据类型,提高代码的灵活性和可重用性。模板在编译时进行类型检查,提供类型安全性,并通过编译时代码生成来提高性能。

1.2 模板的使用机制

C++提供两种模板机制:函数模板类模板

1.2.1 函数模板

函数模板是C++中的一种特殊函数,它可以用于定义通用的函数,可以在不同的数据类型上进行操作。函数模板通过使用模板参数来表示通用的数据类型,从而实现代码的重用和泛型编程。
即告诉编译器,我要声明一个暂时未知函数返回类型与未知形参的函数

函数模板的语法如下所示:

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

template:声明创建了一个模板
typename :表明其后面的符号是一种数据类型
T:一种通用的数据类型,通常为大写字母,可以替换

在上面的语法中,template 表示这是一个函数模板,并且T是一个模板参数,可以是任意类型。返回类型表示函数的返回类型,函数名是函数的名称,参数列表是函数的参数列表。

写一个实例:

template <typename T>
T maximum(T a, T b) {
    return (a > b) ? a : b;
}

int main() {
    int num1 = 10, num2 = 20;
    int maxNum = maximum(num1, num2);
    cout << "Maximum number is: " << maxNum << endl;

    double num3 = 3.14, num4 = 2.71;
    double maxDouble = maximum(num3, num4);
    cout << "Maximum double is: " << maxDouble << endl;

    return 0;
}

二、函数模板的深入学习及注意机制

2.1 函数模板的自动类型推导

在C++中,函数模板可以使用自动类型推导来推断模板参数的类型。通过使用auto关键字作为函数模板的参数类型,编译器可以根据函数调用时的实参类型来推导出模板参数的类型

以下是一个使用自动类型推导的函数模板示例:

template <typename T>
void print(T value) {
    std::cout << value << std::endl;
}

int main() {
    print(10);  // 推导为int类型
    print(3.14);  // 推导为double类型
    print("Hello");  // 推导为const char*类型

    return 0;
}

在上面的示例中,print函数模板使用了自动类型推导,它的模板参数类型使用了auto关键字。在main函数中,我们分别调用了print函数,并传递了不同类型的参数。编译器会根据实参的类型推导出模板参数的类型,并实例化相应的函数。

2.1.1 ①自动类型推导,必须使得推导出的数据类型T是一致的


需要注意的是,自动类型推导仅适用于函数模板的参数类型,而不适用于函数模板的返回类型。

2.2 typename为什么可以替换为class?

在C++中,typename和class在函数模板中都可以用来表示模板参数的类型。它们在函数模板中的使用是等效的,可以互相替换使用

使用typename关键字来表示模板参数的类型是C++标准的做法,特别是在模板的嵌套和依赖名称的情况下。例如,在模板内部使用嵌套类型时,需要使用typename来指示编译器该名称是一个类型。

使用class关键字来表示模板参数的类型是C++早期版本的做法,但在C++标准化过程中,为了更好地表达模板参数是类型的概念,引入了typename关键字。

因此,typename和class在函数模板中的使用是等效的,可以根据个人喜好和代码风格选择使用其中之一。但在一些特定的情况下,如模板的嵌套和依赖名称时,使用typename是必需的

2.3 函数模板的功能及使用时机

函数模板是一种通用的函数定义,可以用于处理多种不同类型的数据。它允许编写一次函数定义,然后根据需要在不同的上下文中使用不同的数据类型。

函数模板的主要功能是实现代码的重用和泛化。通过使用函数模板,可以编写一次通用的函数定义,然后在需要时根据具体的数据类型进行实例化。这样可以避免重复编写相似的代码,提高代码的可维护性和可读性

函数模板的使用时机包括但不限于以下情况:

处理不同类型的数据:当您需要编写一个函数来处理多种不同类型的数据时,可以使用函数模板。例如,您可以编写一个通用的排序函数,可以用于排序整数数组、浮点数数组或字符串数组。

泛化算法:当您需要编写一个通用的算法,可以适用于不同类型的数据结构时,可以使用函数模板。例如,您可以编写一个通用的搜索函数,可以在不同类型的容器中查找特定的元素。

类型安全的操作:函数模板可以提供类型安全的操作,因为它们在编译时进行类型检查。这意味着如果您在函数模板中使用了不兼容的数据类型,编译器将在编译时报错,而不是在运行时出现错误。

代码简化和减少重复:函数模板可以简化代码并减少重复。通过使用函数模板,您可以避免编写多个相似的函数来处理不同类型的数据,从而减少了代码量和维护成本。

三、函数模板具体案例——数组排序

3.1 目的:测试char数组和int数组的排序

如:定义char数组:“badcfe”,要求按照字母表的顺序进行从前往后的排序(即ASCII码数值大小),同时,定义int数组:2 5 8 7 4 1 6 3,要求从小到大排序。
使用函数模板进行泛化处理,提高重用性。

3.2 思路:

采用冒泡排序,每次比较相邻元素的大小,让较大的那个排后面,不断循环。
定义一个函数模板,传参的数组数据类型用模板代替。

template <typename T>

void sort(T arr[], int len) {

    for (int i = 0; i < len - 1; i++) {
        for (int j = 0; j < len - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                // 交换相邻元素
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }

}

3.3 全部代码

#include <iostream>
#include<string>

using namespace std;

template <typename T>

void sort(T arr[], int len) {

    for (int i = 0; i < len - 1; i++) {
        for (int j = 0; j < len - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                // 交换相邻元素
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }

}


int main(void) {


    char chararr[] = { 'b','a','d','c','f','e'};
    int charlen = sizeof(chararr) / sizeof(chararr[0]);
    sort(chararr, charlen);
    std::cout << "按字母表顺序排序后的char数组:";
    for (int i = 0; i < charlen; i++) {
        std::cout << chararr[i] << " ";
    }
    std::cout << std::endl;

    int intarr[] = { 2, 5, 8, 7, 4, 1, 6, 3 };
    int intlen = sizeof(intarr) / sizeof(intarr[0]);
    sort(intarr, intlen);
    std::cout << "从小到大排序后的int数组:";
    for (int i = 0; i < intlen; i++) {
        std::cout << intarr[i] << " ";
    }
    std::cout << std::endl;


	system("pause");
	return 0;
}


四、普通函数与函数模板的区别

定义一个普通函数max,用于比较两个整数的大小并返回较大的数:

int max(int a, int b) {
    return (a > b) ? a : b;
}

接下来,我们定义一个函数模板templateMax,也用于比较两个数的大小并返回较大的数:

template<typename T>
T templateMax(T a, T b) {
  return (a > b) ? a : b;
}

现在,我们来比较普通函数和函数模板的区别。

(1)定义方式:

普通函数的定义是针对特定类型的,如上述的max函数是针对整数类型的定义。

函数模板的定义使用template来声明一个通用的模板函数,可以适用于多种类型。

(2)参数类型:

普通函数的参数类型是具体指定的,如上述的max函数的参数类型是int。

函数模板的参数类型是使用模板参数T来表示的,可以是通用的类型。

(3)重载:

普通函数可以通过函数重载来处理不同类型的参数,如可以定义一个max函数来处理浮点数类型的参数。

函数模板可以通过参数推导来自动匹配不同类型的参数,无需手动重载。例如,我们可以使用templateMax函数模板来比较浮点数、字符等不同类型的参数。

(4)代码生成:

普通函数在编译时会生成具体的函数代码,如上述的max函数在编译时会生成一个针对整数类型的函数。

函数模板在编译时不会生成具体的函数代码,只有在实例化时才会根据具体的类型生成对应的函数代码。例如,当我们使用templateMax函数模板比较整数时,编译器会根据实际情况生成一个针对整数类型的函数。

(5)使用方式:

普通函数可以直接调用,如max(3, 5)。

函数模板需要通过实例化来生成具体的函数,然后才能调用。例如,templateMax(3, 5)会生成一个针对整数类型的函数,并返回较大的数。

五、普通函数和函数模板的调用规则

调用规则如下:
1.如果函数模板和普通函数都可以实现,优先调用普通函数
2.可以通过空模板参数列表来强制调用函数模板
3.函数模板也可以发生重载
4.如果函数模板可以产生更好的匹配优先调用函数模板

接下来一个个解释:

5.1 如果函数模板和普通函数都可以实现,优先调用普通函数

如果函数模板和普通函数都可以实现某个功能,编译器会优先选择普通函数进行调用,而不是实例化函数模板。

这是因为普通函数的匹配更加具体,不需要进行模板参数的推导和实例化过程,所以在编译时会更加高效。而函数模板需要在实例化时根据具体的类型生成对应的函数代码,这个过程可能会增加编译时间和代码体积。

例如,假设我们有一个普通函数foo和一个函数模板templateFoo,它们都可以接受一个整数作为参数:

void foo(int x) {
    // 普通函数实现
     std::cout << "普通函数"<< endl;
}

template<typename T>
void foo(T x) {
    // 函数模板实现
    std::cout << "函数模板"<< endl;
}


int main(void) {

     foo(40);
	system("pause");
	return 0;
}

此时编译器会调用普通函数foo。
当我们调用templateFoo函数模板时,编译器会进行实例化,生成针对具体类型的函数代码:
当我们调用foo函数时,编译器会直接选择普通函数进行调用:

但是,如果没有对应的普通函数实现,或者我们明确指定要调用函数模板,编译器会选择函数模板进行实例化和调用。

那如何告诉编译器去调用函数模板呢?

5.2 可以通过空模板参数列表来强制调用函数模板

即:

foo<>(40);

添加这个符号,就是强制告诉编译器去调用函数模板。

5.3 函数模板也可以发生重载

函数模板也可以发生重载。函数模板的重载与普通函数的重载类似,可以根据参数类型和数量的不同来进行区分。

当存在多个函数模板时,编译器会根据函数调用的参数类型和数量来选择最匹配的函数模板进行实例化和调用。

例如,考虑以下两个函数模板:

template<typename T>
void foo(T x) {
    // 函数模板1
}

template<typename T>
void foo(T x, T y) {
    // 函数模板2
}

当我们调用foo函数时,编译器会根据参数类型和数量来选择最匹配的函数模板进行实例化和调用。

foo(42);        // 调用函数模板1,参数类型为int
foo(3.14, 2.71); // 调用函数模板2,参数类型为double

在这个例子中,根据参数类型和数量的不同,编译器可以正确地选择调用不同的函数模板。

因此,函数模板也可以发生重载,编译器会根据参数类型和数量来选择最匹配的函数模板进行实例化和调用。

5.4 如果函数模板可以产生更好的匹配优先调用函数模板

不解释了,跟5.1是一个道理

六、函数模板的局限性:不是万能的

6.1 问题提出:函数模板无法解决数组、结构体这类特殊传参的比较

对于这段代码:

template<typename T>
void foo(T a,T ,b) {
    a=b;
}

或者:

template<typename T>
void foo(T a,T ,b) {
    if(a>b){
    //
    }
}

传递参数如果是字符型或者整形还好,但是如果a和b是数组的话就不行了,无法运行。或者说a和b是我们用结构体自定义的一种数据类型,那也不行。

那如何解决这类问题呢?

6.2 解决办法

函数模板可以处理数组和结构体这类特殊传参的比较,但需要使用适当的比较操作符或自定义比较函数来实现。

对于数组的比较,你可以使用逐个比较数组元素的方式来判断它们是否相等。例如,考虑以下函数模板:

template<typename T, int size>
bool isEqual(T arr1[size], T arr2[size]) {
    for (int i = 0; i < size; i++) {
        if (arr1[i] != arr2[i]) {
            return false;
        }
    }
    return true;
}

在这个例子中,arr1和arr2是模板参数类型为T的数组,size是数组的大小。

当你调用isEqual函数模板时,它会逐个比较数组元素,并返回比较结果。

int myArray1[] = {1, 2, 3, 4, 5};
int myArray2[] = {1, 2, 3, 4, 5};
bool result = isEqual<int, 5>(myArray1, myArray2);

在这个例子中,myArray1和myArray2作为参数传递给isEqual函数模板时,它们会被自动转换为指向数组首元素的指针,并且编译器会推导出数组的类型和大小。

对于结构体的比较,你可以重载比较操作符或自定义比较函数来实现结构体的比较。例如,考虑以下结构体和函数模板:

struct Point {
    int x;
    int y;
};

template<typename T>
bool isEqual(T obj1, T obj2) {
    return obj1 == obj2;
}

在这个例子中,Point是一个结构体,isEqual函数模板使用了比较操作符==来比较结构体的相等性。

当你调用isEqual函数模板时,它会使用重载的比较操作符或自定义的比较函数来判断结构体是否相等。

Point p1 = {1, 2};
Point p2 = {1, 2};
bool result = isEqual<Point>(p1, p2);

在这个例子中,p1和p2作为参数传递给isEqual函数模板时,它们会被按值传递给函数,并使用重载的比较操作符==来比较结构体的相等性。

总结起来,函数模板可以处理数组和结构体这类特殊传参的比较,但需要使用适当的比较操作符或自定义比较函数来实现。对于数组的比较,你可以逐个比较数组元素;对于结构体的比较,你可以重载比较操作符或自定义比较函数。

七、学习模板并不是为了写模板,而是在STL(标准模板库)能够运用系统提供的模板

当然,模板分为函数模板和类模板,所以下一节讲学习类模板。

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

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

相关文章

【从Python基础到深度学习】7. 使用scp命令实现主机间通讯

一、生成 SSH 密钥对 ssh-keygen 是一个用于生成 SSH 密钥对的命令行工具&#xff0c;用于身份验证和加密通信 ssh-keygen 二、将本地主机上的 SSH 公钥添加到远程主机 ssh-copy-id 命令用于将本地主机上的 SSH 公钥添加到远程主机上的 authorized_keys 文件中&#xff0c;…

反无人机系统技术分析,无人机反制技术理论基础,无人机技术详解

近年来&#xff0c;经过大疆、parrot、3d robotics等公司不断的努力&#xff0c;具有强大功能的消费级无人机价格不断降低&#xff0c;操作简便性不断提高&#xff0c;无人机正快速地从尖端的军用设备转入大众市场&#xff0c;成为普通民众手中的玩具。 然而&#xff0c;随着消…

高斯模糊滤镜

高斯模糊滤镜 import cv2# 读取图像 image cv2.imread(1.jpg)# 高斯模糊滤镜 blurred cv2.GaussianBlur(image, (15, 15), 0)# 保存处理后的图像 cv2.imwrite(blurred_image.jpg, blurred)# 显示原始图像和处理后的图像 cv2.imshow(Original Image, image) cv2.imshow(Blurre…

TiDB in 2023, 一次简单的回顾丨PingCAP 唐刘

2023 年已经过去&#xff0c;TiDB 经过了一年的迭代&#xff0c;又往前进步了一点点&#xff0c;我们非常自豪的看到&#xff0c;TiDB 正在不断地帮助我们的客户成功&#xff0c;包括但不限于&#xff1a; ○ 首个云原生、分布式、全栈国产化银行核心业务系统投产上线丨TiDB …

【日志框架】

日志打印 建议用{}占位而不是字符串拼接打日志前先判断日志级别是否可用&#xff1a; 先根据等级过滤规则再决定写不写&#xff1b;先往一个管道写了内容&#xff0c;但再经等级过滤丢弃&#xff0c;徒增开销。 日志框架 Slf4J Slf4J 不是底层日志框架&#xff0c;只是门面…

JavaScript中解锁Map和Set的力量

&#x1f9d1;‍&#x1f393; 个人主页&#xff1a;《爱蹦跶的大A阿》 &#x1f525;当前正在更新专栏&#xff1a;《VUE》 、《JavaScript保姆级教程》、《krpano》 ​ ​ ✨ 前言 ES6带来了Map和Set两个新的数据结构 - 它们分别用于存放键值对和唯一值。Map和Set提供了更…

第十九篇【传奇开心果系列】Python的OpenCV库技术点案例示例:文字识别与OCR

传奇开心果短博文系列 系列短博文目录Python的OpenCV库技术点案例示例系列 短博文目录前言一、OpenCV 文字识别介绍二、图像预处理示例代码三、文字区域检测示例代码四、文字识别示例代码五、文字后处理示例代码六、OpenCV结合Tesseract OCR库实现文字识别示例代码七、OpenCV结…

PHP+vue+mysql校园学生社团管理系统574cc

运行环境:phpstudy/wamp/xammp等 开发语言&#xff1a;php 后端框架&#xff1a;Thinkphp 前端框架&#xff1a;vue.js 服务器&#xff1a;apache 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat/phpmyadmin 前台功能&#xff1a; 首页&#xff1a;展示社团信息和活动…

COM初体验——新建文档并写入内容。

我想在程序里和Word交互。老师跟我说不要学COM&#xff0c;因为它已经过时了。但是我不想再把代码移植到C#上面&#xff0c;然后用VSTO——已经用了std::unordered_set&#xff01;因为我使用了Copilot&#xff0c;结合我的思考&#xff0c;写了下面的代码&#xff1a; #impor…

JavaWeb学习|JSON与AJAX

学习材料声明 所有知识点都来自互联网&#xff0c;进行总结和梳理&#xff0c;侵权必删。 引用来源&#xff1a;尚硅谷最新版JavaWeb全套教程,java web零基础入门完整版 JSON JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机…

山西电力市场日前价格预测【2024-02-10】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2024-02-10&#xff09;山西电力市场全天平均日前电价为126.73元/MWh。其中&#xff0c;最高日前电价为302.95元/MWh&#xff0c;预计出现在08:15。最低日前电价为0.00元/MWh&#xff0c;预计出…

SolidWorks:创建实体的扇形分割

没找到如何创建扇形分割&#xff0c;自己想了个办法&#xff0c;硬把它分割开了。

新手必看,零基础打造AI数字人,HeyGen完全操作指南

一、HeyGen注册登录教程 1.1 登录HeyGen官网 步骤1&#xff1a;访问官网 在您的浏览器中输入HeyGen官网地址&#xff0c;点击链接进入官网。这是进入HeyGen世界的第一步&#xff0c;一个简洁而直观的界面将会迎接您。 步骤2&#xff1a;点击试用按钮 在首页中&#xff0c;…

Mermaid绘制UML图教程

Mermaid 是一种轻量级的图形描述语言&#xff0c;用于绘制流程图、时序图、甘特图等各种图表。它采用简单的文本语法&#xff0c;使得用户能够快速绘制各种复杂图表&#xff0c;而无需深入学习图形绘制工具。 一、安装Mermaid Mermaid 可以在浏览器中直接使用&#xff0c;也可…

Linux用户常用命令——Linux命令(一)

大家好&#xff0c;从这篇文章开始我将开始进行Linux常用命令的学习&#xff0c;本专栏的每一个知识点我都会尽量在Linux系统中手操实现一遍。如果在读这篇文章的你也想熟悉Linux常用操作命令&#xff0c;我非常推荐你也搭个Linux系统进行实操学习。因为Linux常用的命令的知识点…

Python面向对象学习小记——基本概念

在Python中&#xff0c;int类型、float类型、列表类型、布尔类型等等&#xff0c;都是对象类型。

Linux日志管理服务 rsyslogd

文章目录 1. 基本介绍2. 系统常用的日志3. 日志管理服务 rsyslogd 1. 基本介绍 日志文件是重要的系统信息文件&#xff0c;其中记录了许多重要的系统事件&#xff0c;包括用户的登录信息、系统的启动信息、系统的安全信息、邮件相关信息、各种服务相关信息等。日志对于安全来说…

【MATLAB】鲸鱼算法优化混合核极限学习机(WOA-HKELM)回归预测算法

有意向获取代码&#xff0c;请转文末观看代码获取方式~也可转原文链接获取~ 1 基本定义 鲸鱼算法优化混合核极限学习机&#xff08;WOA-HKELM&#xff09;回归预测算法是一种结合鲸鱼优化算法和混合核极限学习机的混合算法。其原理主要包含以下几个步骤&#xff1a; 初始化&am…

【计算机网络】网际协议——互联网中的转发和编址

编址和转发是IP协议的重要组件 就像这个图所示&#xff0c;网络层有三个主要组件&#xff1a;IP协议&#xff0c;ICMP协议&#xff0c;路由选择协议IPV4 没有选项的时候是20字节 版本&#xff08;号&#xff09;&#xff1a;4比特&#xff1a;规定了IP协议是4还是6首部长度&am…

[python] 罗技动态链接驱动库DLL 控制 键鼠

[python] 罗技动态链接驱动库DLL 控制 键鼠 最近在玩搬砖游戏晶核, 每天有很多重复繁琐的"打卡"操作, 得知隔壁御三家游戏就有大佬做了自动收割的辅助工具,我就想模仿写一个.不过大佬们写的开源工具厉害得多,加了神经网络自动识别,实现寻路和点击功能.我目前最多就是…