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

C++ 函数 模板

1.模板

1.1 模板的概念

模板就是建立通用的模具,大大提高复用性
模板的特点:
1. 模板只是一个框架,不可以直接使用
2. 模板的通用并不是万能的

1.2 函数的模板

1.2.1 函数模板语法

函数模板语法:
建立一个通用函数,其返回值类型和形参可以不具体制定,用一个虚拟的类型代表
语法:
template<typename T>
解释:
template : 声明创建模板
typename: 表明后面的符号是一整数据类型,可以用class 代替
T: 通用的数据类型,名称可以替换,通常为大写字母
// main.cpp
#include <iostream>
#include <string>
using namespace std;

// 函数模板

// 两个整形交换函数
// void swap(int &a, int &b){
//     int temp = a;
//     a = b;
//     b = temp;
// }

// // 两个浮点型交换函数
// void swap(float &a, float &b){
//     float temp = a;
//     a = b;
//     b = temp;
// }

// 函数模板
template <typename T>  // 声明一个模板,告诉编译器后面代码找那个紧跟着的T不要报错,T是一个通用数据类型
void swap1(T &a, T &b){
    T temp = a;
    a = b;
    b = temp;
}

void test01() {
    int a = 10;
    int b = 20;
    // 两种函数调用
    //  1. 自动类型推导
    // swap1(a, b);

    //  2. 指定类型
    swap1<int>(a, b);
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
    
}


int main() {
    test01();
    return 0;
}



1.2.2 函数模板注意事项

函数模板注意事项
自动类型推导,必须推导出一致的数据类型T,才可以使用
模板必须要确定出T的类型,才可以使用
#include <iostream>
#include <fstream>
#include <string>
using namespace std;


// 函数模板注意事项
// 1.自动类型推导,必须推导出一致的数据类型T,才可以使用
template<class T> // typename 可以用class代替
void mySwap(T& a, T& b){
    T temp = a;
    a = b;
    b = temp;
}

// 2.模板必须要确定出T的类型,才可以使用
template<class T>
void func(){
    cout << "func 调用" << endl;
}

void test02() {
    func<int>(); // 必须要确定出T的类型,才可以使用
    // fun();// 不加typename编译器无法推导出T的准确数据类型
}


void test01() {
    int a = 10;
    int b = 20;
    char c = 'c';
    // 利用自动类型推导,不加typename编译器无法推导出T的准确数据类型
    mySwap(a, b);
    // mySwap(a, c); // 编译器无法推导出T的准确数据类型
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
}

int main(int argc, char const *argv[]) {
    test02();
    return 0;
}

总结:使用模板时必须确定出通用数据类型T,并且能够推导出一致的类型

1.2.3 函数模板案例

实现通用 对数组进行排序的函数
规则 从大到小
算法 选择排序
测试 char数组 int数组 自定义数据类型数组
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

// 交换函数模板
template<class T>
void mySwap(T &a, T &b){
    T temp = a;
    a = b;
    b = temp;
}

// 排序算法
template<class T>
void mySort(T arr[], int len){
    for (int i = 0; i < len; i++)
    {
        // 记录下标
        int index = 0;
        for (int j = 1; j < len - i; j++)
        {
            // 从大到小 或者从小到大,只需要更改这里
            if (arr[j] < arr[index])
            {
                index = j;
            }
            
        }
        mySwap(arr[len - i - 1], arr[index]);
    }
    
}

// 打印数据模板
template<class T>
void printArray(T arr[], int len){
    for (size_t i = 0; i < len; i++)
    {
        cout << arr[i] << " ";
    }
    
}

void test01(){
    // char数组
    char charArr[] = "headbfgsvw";
    int len = sizeof(charArr) / sizeof(char);
    mySort(charArr, len);
    printArray(charArr, len);
    cout << endl;
    // int数组
    int intArr[] = {1, 3, 5, 7, 9, 2, 4, 6, 8, 0};
    len = sizeof(intArr) / sizeof(int);
    mySort(intArr, len);
    printArray(intArr, len);
}
int main(int argc, char const *argv[]) {
    test01();
    return 0;
}

1. 2.4 普通函数和函数模板的区别

普通函数和函数模板的区别:
1. 普通函数调用时可以发生隐式类型转换(隐式类型推导)
2. 函数模板调用时,如果利用实参进行类型推导,不会发生隐式类型转换
3. 如果利用显示指定类型的方式,可以发生隐式类型转换
#include <iostream>
#include <fstream>
#include <string>
using namespace std;

    
// 普通函数
int myAdd(int a, int b){
    return a + b;
}
// 函数模板
template<class T>
T myAddO2(T a, T b){
    return a + b;
}

void test01(){
    int a = 10;
    int b = 20;
    char c = 'c';
    cout << "普通函数调用结果:" << myAdd(a, c) << endl;
    // 自动类型推导,不能发生隐式类型转换
    // cout << "函数模板调用结果:" << myAddO2(a, c) << endl; 
    // 显示指定类型,可以发生隐式类型转换
    cout << "函数模板调用结果:" << myAddO2<int>(a, c) << endl;
}
int main(int argc, char const *argv[]) {
    test01();
    return 0;
}

2.5 模板函数与普通函数的调用规则

模板函数与普通函数的调用规则
1. 如果普通函数和模板函数都可以调用,优先调用普通函数
2. 可以通过空模板参数列表来强制调用模版函数
3. 函数模板可以发生函数重载
4. 如果函数模板可以产生更好的匹配,优先调用函数模板
#include <iostream>
#include <fstream>
#include <string>
using namespace std;


// 普通函数
void myPrint(int a, int b){
    cout << "普通函数调用" << "a = " << a << ", b = " << b << endl;
}

// 函数模板
template <typename T>
void myPrint(T a, T b){
    cout << "函数模板调用" << "a = " << a << ", b = " << b << endl;
}

// 函数模板
template <typename T>
void myPrint(T a, T b, T c){
    cout << "重载函数模板调用" << "a = " << a << ", b = " << b  << ", c = " << c << endl;
}

void test01(){
    int a = 10;
    int b = 20;
    int c = 'c';
    myPrint(a, b); // 普通函数调用
    myPrint<>(a, b); // 通过空模板参数列表,强制调用模板函数
    myPrint(a, b, c); // 重载函数模板调用
    // 如果函数模板可以产生更好的匹配,优先调用函数模板
    char ch = 'a';
    char ch2 = 'b';
    myPrint(ch, ch2); // 函数模板调用
}
int main(int argc, char const *argv[]) {
    test01();
    return 0;
}

// 总结
// 既然提供了函数模板,最好旧不要提供普通函数,否则容易出现二义性

1.2.6 模板的局限性

局限性:
模板的通用性并不是万能的

#include <iostream>
#include <fstream>
#include <string>
using namespace std;


//  模板的局限性
// 模板不是万能的,有些特定数据类型,需要用具体化方式做特殊处理。

//对比两个数据是否相等
template<class T>
bool compare(const T& v1, const T& v2){
    if (v1 == v2)
    {
        return true;
    }
    else
    {
        return false;
    }
    
}

class Person{
public:
    Person(string name, int age):name(name),age(age){};
    string name;
    int age;
};

// 针对Person类特化版本,具体化优先调用
template<> bool compare(const Person& p1, const Person& p2){
    if (p1.name == p2.name && p1.age == p2.age){
        return true;
    }
    return false;
}



void test01(){
    int a = 10;
    int b = 20;
    bool ret = compare(a, b);
    if (ret)
    {
        cout << "a == b " << endl;
    }
    else{
        cout << "a != b" << endl;
    }
}


void test02(){
    Person p1("Tom", 10);
    Person p2("Tom", 10);
    bool ret = compare(p1, p2);
    if (ret)
    {
        cout << "p1 == p2" << endl;
    }
    else{
        cout << "p1 != p2" << endl;
    }
}
int main(int argc, char const *argv[]) {
    test02();
    return 0;
}


总结

1. 利用具体化模板,可以解决自定类型的通用化
2. 学习模板并不是为了写模板,而是在STL能够运用系统提供的模板

http://www.kler.cn/a/473240.html

相关文章:

  • MySQL insert or update方式性能比较
  • 超完整Docker学习记录,Docker常用命令详解
  • uniapp vue2版本如何设置i18n
  • [Linux]redis5.0.x升级至7.x完整操作流程
  • 服务器漏洞修复解决方案
  • 【python】matplotlib(radar chart)
  • 代码重构 - 规范
  • 【Dify】Dify自定义模型设置 | 对接DMXAPI使用打折 Openai GPT 或 Claude3.5系列模型方法详解
  • SAP销售订单与MRP的另一个关联点:需求类型
  • <代码随想录> 算法训练营-2025.01.04
  • 动手学深度学习11.4. 随机梯度下降-笔记练习(PyTorch)
  • JavaScript系列(14)--元编程技术
  • WebSocket 服务端开发:Node.js 实战
  • 备战春招—FPGA 2024年的面试题库
  • 网络传输层TCP协议
  • Java-编写的一个生产者-消费者模式
  • docker-compose部署下Fastapi中使用sqlalchemy和Alembic
  • CST软件如何设置分布式计算(Distributed Computing)的 TCP-IP子网
  • Redis 笔记(二)-Redis 安装及测试
  • (长期更新)《零基础入门 ArcGIS(ArcScene) 》实验七----城市三维建模与分析(超超超详细!!!)
  • 运行vue项目,显示“npm”无法识别为 cmdlet、函数、脚本文件或可操作程序的名称
  • 腾讯云AI代码助手-每日清单助手
  • Python----Python爬虫(selenium的使用,定位元素,层级定位)
  • 每日一题-两个链表的第一个公共结点
  • 阿里云人工智能平台图像视频特征提取
  • python注意事项:range遍历越索引现象、列表边遍历边修改出现的问题