C++之函数模板进阶

96 阅读4分钟

前言

当我们了解了如何去创建一个函数模版了后,我们需要继续去探究下函数模板的使用究竟还有哪些注意点,因为世间万事万物都有好的一面和不好的一面。我们当然也深知淮南为橘,淮北为枳的道理。函数模版虽然在某些情况下使用比较方便,但是并不代表他在所有的情况下使用都很方便,本文要介绍的就是函数模版的使用注意事项,函数模版和普通函数有啥区别,它有啥局限性等内容。

示例解析

函数模板使用注意事项

示例:我们以交换两个数的值演示,我们将交换两个数的函数写成函数模板的方式。 先看下所有代码:

#include<iostream>
using namespace std;

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

void test(){
    int a = 10;
    int b = 20;
    char c = 'c';
    //mySwap(a,c);//错误,推导不出一致的T类型
    //mySwap(a,b);// 正确
    cout<< "a = " << a << endl;
    cout<<"b = " << b<< endl;
}

//2、摸板必须确定出T的数据类型才可以使用
template<class T>
void func()
{
    cout << "func=====" << endl;
} // 函数体中没有用到T

void test1(){
   // func();//错误,没有与参数列表匹配的 函数模板 "func"
   func<int>();
}

int main(){
    test1();
    system("pause");
    return 0;
}

函数模板在使用的时候有两种方式,一是自动类型推导,二是显示指定类型。在我们使用自动类型推导的方式时需要注意,必须推导出一致的数据类型T才可以使用。如下所示:

交换两个数的函数模板写法:

template<typename T> 
void mySwap(T &a,T &b){
    T temp = a;
    a = b;
    b = temp;
}

当我们使用的时候:

void test(){
    int a = 10;
    int b = 20;
    char c = 'c';
    //mySwap(a,c);//错误,推导不出一致的T类型

    //mySwap(a,b);// 正确

    cout<< "a = " << a << endl;
    cout<<"b = " << b<< endl;
}

如上面的代码所示,int a = 10;int b =20; 这时候调用函数mySwap(a,b);是可以的,但是若是这时候我们 有一个新变量: char c = 'c' 这时候再调用ySwap(a,c); 是不可以的,因为无法推导出一致的T类型,也就是编译器这时候不知道该推导成int 还是char

函数模板使用除了上面的注意事项,还有一点需要注意,那就是模版必须确定出T的数据类型才可以使用 比如此时我们定义了一个函数模板,但是我们并没有使用到函数模板定义的虚拟数据类型T,这时使用函数模板时必须显示指定数据类型:

我们定义了一个函数模板,但是函数体没有使用虚拟数据类型T

template<class T>
void func()
{
    cout << "func=====" << endl;
} 

当我们调用函数时:

void test1(){
   // func();//错误,没有与参数列表匹配的 函数模板 "func"
   func<int>();
}

上面的注释已经写了,调用函数的时候不能使用func();方式,虽然函数体没有使用定义的虚拟数据类型T,但是使用的时候也必须制定数据类型,func<int>();,当然这种情况在我们开发中会很少遇到,因为我们不使用函数模版的话是不会定义的

普通函数和函数模版的区别

普通函数和函数模版的区别有三点:

1.普通函数调用可以发生隐式类型转换

2.函数模版,用自动类型推导的时候,不可以发生隐式类型转换

3.函数模版使用显示指定类型,可以发生隐式类型转换

我们可以随便定义一个函数模版和一个普通函数,看下他们的区别,代码如下:

// 普通函数
int add(int a,int b)
{
    return a+b;
}

// 函数模版
template<typename T>
T addUseTepmlate(T a,T b){
    return a+b;
}

void test(){
    int a = 10;
    int b = 20;
    
    char c = 'c';// a---97 c --- 99 
    cout<< add(a,b) << endl;//没有发生隐式类型转换
     cout<< add(a,c) << endl;// 隐式类型转换
     
     //自动类型推导
     cout << addUseTepmlate(a,b)<<endl;
      // cout << addUseTepmlate(a,c)<<endl; 错误,不会发生隐式类型转换
      // 显示指定类型调用模版
       cout << addUseTepmlate<int>(a,c)<<endl;// 可以调用成功,会发生隐式类型转换
}
int main(){
    test();
    return 0;
}

在上面的代码注释中我们已经对函数模版和普通函数的区别做了分析,由此我们可以得出一个结论,既然提供了函数模版,就不要提供与其功能相同的普通函数,容易产生二义性

总结

本文介绍了函数模板使用过程中的注意事项,一是自动类型推导必须推导出一致的数据类型才可以使用,二是模板必须要确定出T的数据类型才可以使用。另外,函数模板虽然可以解决自定义类型的通用化,但是我们学习模板并不是为了写模板,而是为了更好的使用函数模板。