【C++】函数模板

来源:互联网 发布:美工捅死策划 编辑:程序博客网 时间:2024/04/28 07:55

函数模板

所谓模板,是C++中实现多态的又一方法。我们已经知道函数重载可以实现多态,让一个函数名多用。这样可以简化函数的调用形式,但是又必须定义每一个函数。

C++提供模板正是为了简化这一个过程。

探索函数重载的缺点

首先,让 我们写一个Add函数

int Add(int x,int y){return x - y;}

ok,现在我们要对Add函数进行重载

char Add(char x, char y){return x - y;}

我们新定义了一个日期类型Date,我们又得对Date进行重载

Date Add(Date& d1, Date &d2){return d1 - d2;}

额,我们为什么如此的粗心。。。把“+”写成了“-”

这样的话,我们需要修改所有重载函数的逻辑。这就是函数重载的缺点

虽然只需要把所有的加号换过来就好了,可是由于我们写的是非常简单的函数(只有一条返回语句)。当我们写了很长很长的语句时,这就会有很大的工作量了

函数重载的缺点:

(1)每当需要新的类型出现后,需要重载对应添加的函数

(2)函数重载的各个函数,大体函数逻辑一样,只是改变了类型。导致代码的利用率不高。

(3)当逻辑出现问题时,需要修改所有重载的函数。不方便维护

(4)函数重载无法解决返回值的问题

泛型编程

由于函数重载的诸多缺点,我们引入了【泛型编程】的概念。所谓的【泛型编程】,就是编写与类型无关的代码,是提高代码复用的一种手段。

而泛型编程,又包括【函数模板】【类模板】。模板是泛型编程的基础!


函数模板

关键字 template

函数模板定义格式:

template<typename T>T Add(T x,T y){return x + y;}

注意:

(1)函数模板不是类或者函数,它只是一个蓝图。只有在模板实例化后编译器才会为之生成对应的类或者函数。

(2)模板会被编译两次,第一次检查模板里的语法是否有错,如缺少分号;第二次则是在实例化的时候,会查看所有的调用是否都是有效的。

参数转换

编译器只能执行两种转换

1、const转换

接收const对象指针或者const对象引用的参数,可以用非const对象的指针或引用进行调用

template<typename T>T Add(const T& x,const T& y){return x + y;}int main(){int a = 10;int b = 20;int &ra = a;int &rb = b;cout << "a + b =" << Add(ra, rb) << endl;system("pause");return 0;}
2、函数或者数组到指针的转换

如果模板不是引用类型的话,那么它对函数或者数组类型的实参应转为指针

template <typename T>T Add(const T* x, const T* y){return (*x + *y);}int main(){int a[5] = {0};int b[5] = {1,2,3,4,5};cout << "a + b = " << Add(a, b) << endl;return 0;}

模板参数

包括两种类型,分别是类型形参和非类型形参

模板形参的名字只能在模板形参之后,到模板的声明和定义的结束使用,遵循就近原则。

template<typename T>//FunTest函数模板void FunTest(T t){cout << "t's type:"typeid(t).name() << endl;cout << "global's type:"typeid(global).name() << endl;}T global;int main(){FunTest<int>(5);return 0;}

注意:

1、模板形参的名字在同一模板参数列表内只能使用一次

template<typename T,typename T>void FunTest(T a,T b){return;}


2、所有模板前必须加上class或者typename关键字进行修饰

template<typename T,U>void FunTest(T a,U b){return;}

函数模板的重载

1、函数模板和函数模板进行重载

当然,函数模板也是可以重载的。

template<typename T>T Max(const T& left, const T& right){return left>right ? left : right;}template<typename T>T Max(const T& a, const T& b, const T& c){return Max(Max(a, b), c);};int main(){cout << Max(1, 2) << endl;cout << Max(1.2, 3.4, 5.6) << endl;system("pause");return 0;}
上面我们重载了两个求最大值的函数模板,他们只有模板的参数不同而已

2、函数模板和非模板的函数进行重载

int Max(const int& left, const int & right)//普通函数  {return left>right ? left : right;}template<typename T>T Max(const T& left, const T& right){return left>right ? left : right;}template<typename T>T Max(const T& a, const T& b, const T& c){return Max(Max(a, b), c);};int main(){cout << Max(10, 20, 30) << endl;cout << Max<>(10, 20) << endl;cout << Max(10, 20) << endl;cout << Max(10, 20.12) << endl;cout << Max<int>(10.0, 20.0) << endl;cout << Max(10.0, 20.0) << endl;system("pause");return 0;}
注意:在此种情况下,编译器会先在非模板的函数中搜寻与之参数类型,个数完全匹配的函数;如果找到了,编译器便会调用它,不会让函数模板进行实例化;

如果没有找到,则通过函数模板进行实例化;如果想让编译器不调用非模板函数,则需要在调用的时候进行显示实例化,这样的话,函数模板实例化生成的代码也和原来定义的

非模板函数不是同一块代码

模板函数的特化

有时候,不能写出对所有可能实例化出来的类型都最适合的模板,在某些情况下,通过模板定义的函数有可能对于某个类型是错误的,或者编译失败,或逻辑错误

    template <typename T>      int Compare(T s1, T s2)       {          if(s1<s2)              return -1;          else if(s1>s2)              return 1;          else               return 0;      }      int main()      {          char* str1 = "abcd";          char* str2 = "ghf";          cout<<Compare(str1,str2)<<endl;          system("pause");          return 0;      }  
如果这样写我们的代码,那么,你就会发现比较的结果一直是不变的

对此,我们只需要特化一下~

template<>int Compare<const char*>(const char* str1,const char *str2){return strcmp(str1, str2);}
就可以避免错误的发生

注意:特化一定要和原模板函数版本的形参类型完全一致

1 0
原创粉丝点击