C++ 函数 模板

1.模板

1.1 模板的概念

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

1.2 函数的模板

1.2.1 函数模板语法

函数模板语法:
建立一个通用函数,其返回值类型和形参可以不具体制定,用一个虚拟的类型代表
语法:
template<typename T>
解释:
template : 声明创建模板
typename: 表明后面的符号是一整数据类型,可以用class 代替
T: 通用的数据类型,名称可以替换,通常为大写字母
cpp 复制代码
// 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的类型,才可以使用
csharp 复制代码
#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数组 自定义数据类型数组
csharp 复制代码
#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. 如果利用显示指定类型的方式,可以发生隐式类型转换
csharp 复制代码
#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. 如果函数模板可以产生更好的匹配,优先调用函数模板
csharp 复制代码
#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 模板的局限性

局限性:

模板的通用性并不是万能的

csharp 复制代码
#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能够运用系统提供的模板
相关推荐
qq_4419960510 分钟前
Java 抽象类与接口的成员定义和区别总结
java·开发语言
7yewh11 分钟前
【LeetCode】力扣刷题热题100道(6-10题)附源码 相交链表 回文链表 反转链表 合并链表 移动零(C++)
c语言·数据结构·c++·算法·leetcode·链表·贪心算法
程序员老冯头14 分钟前
第三十六章 C++ Web 编程
开发语言·c++·microsoft
被迫学习Java19 分钟前
在Java中实现集合排序
java·开发语言·windows
bohu8322 分钟前
4.5 在C++节点中使用参数
c++·ros2·参数通信
DARLING Zero two♡27 分钟前
【优选算法】Simulation-Phoenix:模拟算法的重生涅槃
java·数据结构·c++·算法·leetcode
Crossoads27 分钟前
【汇编语言】外中断(三)—— 探秘汇编外中断:从安装新INT 9例程到指令系统总结
android·开发语言·汇编·stm32·单片机·嵌入式硬件·dubbo
小林熬夜学编程30 分钟前
【Linux网络编程】第二十二弹---深入理解 I/O 多路转接之 epoll:系统调用、工作原理、代码演示及应用场景
linux·运维·服务器·开发语言·网络·c++
忘梓.43 分钟前
解锁动态规划的奥秘:从零到精通的创新思维解析(5)
算法·动态规划
半桶水专家1 小时前
go函数的参数怎么设置默认值
开发语言·后端·golang