目录
函数模板和普通函数的区别
先举个demo:
template <typename T>
void myswap(T &a, T &b)
{
T c = 0;
c = a;
a = b;
b = c;
cout << "hello ....我是模板函数 欢迎 calll 我" << endl;
}
这是我们学习函数模板的第一个案例,假设在这个的基础之上,我们再写一个与其同名的函数:
void myswap(int a, char c)
{
cout << "a:" << a << "c:" << c << endl;
cout << "我是普通函数 欢迎来访" << endl;
}
一观察就可以发现,上面两个函数同名,参数类型不一样,很显然它俩是函数重载的关系。
复习一下:重载是发生在同一个作用域里面,重写是发生在父类与子类直接,而且,重写又可以细分成虚函数重写(将产生多态)和重定义(不是虚函数重写)。
那么当调用上面的两个函数的时候,编译器优先调用那个函数呢?所以,作一下探讨:
先尝试着调用一下:
void main()
{
int a = 10;
char c = 'z';
myswap(a, c); // 普通函数的调用: 可以进行隐式的类型转换
myswap(c, a); //
myswap(a, a); // 函数模板函数的调用(本质:类型参数化): 将严格的按照类型进行匹配,不会进行自动类型转换
cout<<"hello..."<<endl;
system("pause");
return ;
}
运行后,结果发现,调用第一个函数"myswap(a, c); "如我们所期,进入了普通函数里面了,这是因为我们的两个参数都与普通函数相对应,而且,函数模板的函数要求对应的两个参数都应该是同一个类型的,所以,肯定不会调用到函数模板的函数中去;
调用第二个函数"myswap(c, a); ",发现第二个函数还是调用到普通函数当中去了,因为函数模板的函数要求函数形参与实参的类型必须要相同,但是,"c"与"a"的形参与实参不相同,所以,肯定不会调用到函数模板创建的函数当中,这就说明函数模板所创建的函数执行的时候,会进行严格的类型匹配,但是呢,普通的函数会有一个隐式的类型转换;
再看调用第三个函数"myswap(a, a); ",这函数运行是,函数有可能进行隐式的类型转换,也有可能去调用函数模板创建的函数(因为这两个参数的函数类型都一样啊),经过编译器验证,调用的是函数模板所创建的函数,这是因为第三个函数严格匹配函数模板所创建的函数形式。
普通函数与模板函数的本质区别
当调用函数模板所创建的函数(本质:类型参数化)将严格的按照类型进行匹配,不会自动进行类型的转换,但是普通函数调用的时候,可以进行自动的隐式的类型转换。
普通函数与模板函数在一起时的调用规则
编译规则
- 函数模板可以像普通函数一样被重载
- C++编译器优先考虑普通函数
- 如果函数模板可以产生一个更好的匹配,那么选择模板
- 可以通过空模板实参列表的语法限定编译器只通过模板匹配
举例说明
看如下三个函数,一次命名为(为了下面好区分,简单的做个命名)A,B,C函数:
int Max(int a, int b)
{
cout<<"int Max(int a, int b)"<<endl;
return a > b ? a : b;
}
template<typename T>
T Max(T a, T b)
{
cout<<"T Max(T a, T b)"<<endl;
return a > b ? a : b;
}
template<typename T>
T Max(T a, T b, T c)
{
cout<<"T Max(T a, T b, T c)"<<endl;
return Max(Max(a, b), c);
}
主调函数进行调用:
void main()
{
int a = 1;
int b = 2;
cout<<Max(a, b)<<endl; //当函数模板和普通函数都符合调用时,优先选择普通函数
cout<<Max<>(a, b)<<endl; //若显示使用函数模板,则使用<> 类型列表
cout<<Max(3.0, 4.0)<<endl; //如果 函数模板产生更好的匹配 使用函数模板
cout<<Max(5.0, 6.0, 7.0)<<endl; //重载
cout<<Max('a', 100)<<endl; //调用普通函数 可以隐式类型转换
system("pause");
return ;
}
下面来分析,看第一个函数"cout<<Max(a, b)<<endl;",一观察就可以发现,这个函数的形参为两个,而且数据类型一样,所以,肯定是在A和B这两个函数之间考虑了,这个时候,普通函数要比模板函数更优先使用,这是因为普通函数的调用场景已经有了,如果选择模板函数,还需要先生成对应的类型,所以,会优先执行A函数,即普通函数。
再看第二个函数"cout<<Max<>(a, b)<<endl;",这样就是强制告诉编译器,要使用B(模板)函数。
再看第三个函数"cout<<Max(3.0, 4.0)<<endl;",这个输入的值是浮点数类型,跟普通函数的默认类型不匹配,如果隐式的将浮点类型转为整型会造成数据的失真,这个时候编译器会优先使用A(模板)函数。
再看第四个函数"cout<<Max(5.0, 6.0, 7.0)<<endl;"这个有三个参数,A(普通)函数,无论如何无法满足需求,只有C函数能满足需求,毫无疑问调用C(三个参数的)函数。
最后一个函数"cout<<Max('a', 100)<<endl; ",第一个参数类型是"char",第二个参数类型"int"类型的,这个时候,模板函数因为会严格执行类型匹配无法满足我们的需求,故会调用A(普通)函数来执行。
模板函数与普通函数在一起时的调用规则
- 当函数模板和普通函数都符合调用时,优先选择普通函数
- 若显示使用函数模板,则使用<> 类型列表
- 如果函数模板产生更好的匹配,则使用函数模板
- 调用普通函数,可以隐式类型转换,而模板函数必须严格执行类型匹配,不能不一样。
总体代码
dm03_函数模板遇上函数重载.cpp
#include <iostream>
using namespace std;
//让 类型参数化 ===, 方便程序员进行编码
// 泛型编程
//template 告诉C++编译器 我要开始泛型编程了 .看到T, 不要随便报错
template <typename T>
void myswap(T &a, T &b)
{
T c = 0;
c = a;
a = b;
b = c;
cout << "hello ....我是模板函数 欢迎 calll 我" << endl;
}
void myswap(int a, char c)
{
cout << "a:" << a << "c:" << c << endl;
cout << "我是普通函数 欢迎来访" << endl;
}
void main()
{
int a = 10;
char c = 'z';
myswap(a, c); // 普通函数的调用: 可以进行隐式的类型转换
myswap(c, a); //
myswap(a, a); // 函数模板函数的调用(本质:类型参数化): 将严格的按照类型进行匹配,不会进行自动类型转换
cout<<"hello..."<<endl;
system("pause");
return ;
}
dm04_函数模板和普通函数在一起调用规则研究.cpp
/*
函数模板不允许自动类型转化
普通函数能够进行自动类型转换
*/
/*
1 函数模板可以像普通函数一样被重载
2 C++编译器优先考虑普通函数
3 如果函数模板可以产生一个更好的匹配,那么选择模板
4 可以通过空模板实参列表的语法限定编译器只通过模板匹配
*/
#include "iostream"
using namespace std;
int Max(int a, int b)
{
cout<<"int Max(int a, int b)"<<endl;
return a > b ? a : b;
}
template<typename T>
T Max(T a, T b)
{
cout<<"T Max(T a, T b)"<<endl;
return a > b ? a : b;
}
template<typename T>
T Max(T a, T b, T c)
{
cout<<"T Max(T a, T b, T c)"<<endl;
return Max(Max(a, b), c);
}
void main()
{
int a = 1;
int b = 2;
cout<<Max(a, b)<<endl; //当函数模板和普通函数都符合调用时,优先选择普通函数
cout<<Max<>(a, b)<<endl; //若显示使用函数模板,则使用<> 类型列表
cout<<Max(3.0, 4.0)<<endl; //如果 函数模板产生更好的匹配 使用函数模板
cout<<Max(5.0, 6.0, 7.0)<<endl; //重载
cout<<Max('a', 100)<<endl; //调用普通函数 可以隐式类型转换
system("pause");
return ;
}