博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C++学习之路: 模板函数
阅读量:6568 次
发布时间:2019-06-24

本文共 6449 字,大约阅读时间需要 21 分钟。

1 #include 
2 #include
3 #include
4 using namespace std; 5 6 template
7 T max(T a, T b) 8 { 9 return a > b ? a : b;10 }11 12 13 14 int main(int argc, const char *argv[])15 {16 int i = 42;17 cout << ::max(7, i) << endl;18 19 double f1 = 3.4;20 double f2 = -6.7;21 cout << ::max(f1, f2) << endl;22 23 string s1 = "hello";24 string s2 = "world";25 cout << ::max(s1, s2) << endl;26 27 28 return 0;29 }30 //次模板函数,是值拷贝型, 输入和返回值都是值拷贝,开销巨大, 如果我们 输入和返回值都是类类型的话, 对资源的消耗是巨大的。31 所以一般用下面这种模板
#include 
#include
#include
using namespace std;template
const T &max(const T &a,const T &b){ return a > b ? a : b;}int main(int argc, const char *argv[]){ int i = 42; cout << ::max(7, i) << endl; double f1 = 3.4; double f2 = -6.7; cout << ::max(f1, f2) << endl; string s1 = "hello"; string s2 = "world"; cout << ::max(s1, s2) << endl; return 0;}

上面这种模板,会节约很多。

 

下面介绍一种有坑的情况

1 #include 
2 #include
3 #include
4 using namespace std; 5 6 template
7 const T &max(const T &a, const T &b) 8 { 9 return a > b ? a : b;10 }11 12 13 14 int main(int argc, const char *argv[])15 {16 //编译错误17 cout << ::max(4, 4.7) << endl;18 return 0;19 }

我们输入为 4-int  4.7-double  , 编译器无法找到一个模板去匹配, 编译错误。

因为我们模板是两个参数都是T类型的,  而不是一个是T1,另一个是T2;

 

好吧,我们尝试一下两个参数类型是不同的模板

1 #include 
2 #include
3 #include
4 using namespace std; 5 6 template
7 const T1 &max(const T1 &a, const T2 &b) 8 { 9 return a > b ? a : b;10 }11 12 13 14 int main(int argc, const char *argv[])15 {16 cout << ::max(3, 4.5) << endl; 17 // int double18 // const int &max(const int &, const double &);19 // 因为T1与T2类型不同,所以进行强制类型转换20 // 产生了一个中间临时变量21 // 所以最后的返回值,引用了一个临时变量22 23 return 0;24 }

上面是一个错误了例子, 编译不会通过, 错误十分隐晦, 我们看到 参数1是 T1, 参数2 是T2, 

 

我们输入时 参数a是3 --int  参数b是4.5--double,那么我们告诉编译器去生成一个int &max(const int &, const double &)的函数,

返回值和 参数一都是T1==int, 显然 4.5 是比3大的,  所以返回的是4.5,但是我们要求返回值是int型, 显然也不匹配, 所以编译器会进行,强制转换 生成一个tmp = (int)4.5;

return  tmp的引用。  tmp是临时变量, 该函数调用结束后栈空间被释放, 返回临时变量的引用是没有意义的。

所以导致编译错误    

 

 

 

下面看一下模板的重载

 

例子代码:

1 #include 
2 #include
3 #include
4 5 using namespace std; 6 7 8 9 const int &max(const int &a, const int &b)10 {11 return a > b ? a : b ;12 }13 template
14 15 const T &max(const T &a, const T &b)16 {17 return a > b ? a: b ;18 }19 20 template
21 const T &max(const T &a, const T &b, const T &c)22 {23 return ::max(::max(a, b), c) ;24 }25 26 27 28 29 int main(int argc, const char *argv[])30 {31 ::max(7, 42, 68) ; //调用第三个32 ::max(7.0, 43.5) ; //调用第二个33 ::max('a', 'b') ; //调用第二个34 ::max(7, 42) ; //调用第一个,其实第二个也可以,但是取最匹配的35 ::max<>(7, 42) ; //指定从模板2 进行匹配, 根据模板生成一个 int &max(const int &, const int &)36 cout << ::max
(7,42) <

总之不需要死记调用的规则,  掌握一个原则, 编译器总是选择调用最合适的那个函数。

 

1 #include 
2 #include
3 #include
4 using namespace std; 5 6 7 8 template
9 const T &max(const T &a, const T &b)10 {11 return a > b ? a : b ;12 }13 14 template
15 const T *max(const T *a, const T *b)16 {17 return *a > *b ? a : b ;18 }19 20 const char *max(const char *a, const char *b)21 {22 return ::strcmp(a, b) > 0 ? a : b ;23 }24 25 26 27 int main(int argc, const char *argv[])28 {29 int a = 7 ;30 int b = 42 ;31 ::max(a, b); //调用第一个,上面3个模板 最匹配的显然是第一个。32 33 string s = "hey" ;34 strubg t = "world" ;35 ::max(s, t) ; //调用第一个 最匹配的显然还是第一个36 37 int *p1 = &7 ;38 int *p2 = &8 ;39 ::max(p1, p2) ; //调用第二个, 当参数是指针时, 优先调用指针为参数的模板,最匹配为第二个40 41 const char *s1 = "fucker" ;42 const char *s2 = "asshole" ;43 ::max(s1, s2) ; //调用第3个。 2和3都是 指针为参数的模板, 但是第三个更精确。所以调用第三个44 return 0;45 }

根据上面几个例子, 我们可以总结出几个规律,这个规律是编译器 合成模板函数, 调用的规则, 这是C++最晦涩的部分之一, 其编译器实现是十分复杂的,集结的前人无数心血

a)      当条件相同时,优先选择非模板函数。例如::max(7, 42);

b)     在强制类型转化,与实例化模板可行之间,优先选择实例化模板。::max(7.0, 43.5); ::max('a', 'b');

c)      实例化版本不可行,则去尝试普通函数的转化,例如::max('a', 42.7);

d)     参数是指针时,优先选择指针版本。

e)      总之,尽可能采用最匹配的版本。

 

下面介绍一个陷阱

 

1 #include 
2 #include
3 #include
4 #include
5 using namespace std; 6 7 8 template
9 const T &max(const T &a, const T &b)10 {11 return a > b ? a : b ;12 }13 const char *max(const char * a, const char *b)14 {15 return ::strcmp(a, b) > 0 ? a : b ;16 }17 18 template
19 const T &max(const T &a, const T &b, const T &c) //三参数版本的模板函数, 要根据情况来调用 上面两个20 {21 return ::max(::max(a, b), c) ; //调用1 或者 222 }23 24 int main(int argc, const char *argv[])25 {26 ::max(7 ,42, 68) ; //ok --> 当三调用 1 时, 1返回的也是引用, 3 再接收这个引用,在返回给主函数27 28 const char *p1 = "hello" ;29 const char *p2 = "world" ;30 const char *p3 = "fuck" ;31 32 ::max(p1, p2 , p3) //--》这个3 要调用2, 而返回的是一个指针的拷贝(指针拷贝就是, 新创建一个指针, 指向旧指针的指向) 33 //像这个 p1 --> a <-- p2 ; p1 是== p2的, 解引用后内容一样,但是指针是不同的。 34 return 0; //当 3 再把 2 返回的一个临时指针(局部变量) 当引用 传出, 就出现了编译错误35 }

 

上述得出结论:

在模板函数重载中,不要混合使用传值和传引用。尽可能使用传引用。

会导致产生临时变量, 以至于外层的函数当做引用 传出。 解决上面BUG的方法就是, 把函数2 的返回值和输入参数加上引用 。

 

1 #include 
2 #include
3 #include
4 #include
5 using namespace std; 6 7 //传引用 8 template
9 const T &max(const T &a, const T &b)10 {11 return a > b ? a : b;12 }13 14 const char *&max(const char *&a, const char *&b)15 {16 return ::strcmp(a, b) > 0 ? a : b;17 }18 19 //求3个任意类型的最大值20 //传引用21 template
22 const T &max(const T &a, const T &b, const T &c)23 {24 return ::max(::max(a, b), c);25 //::max(::max(s1, s2), s3); //返回的是一个临时的变量26 //这里将临时变量的引用返回出去,可能导致错误27 //const char *temp = ::max(::max(s1, s2), s3);28 //29 }30 31 32 int main(int argc, const char *argv[])33 {34 ::max(7, 42, 68);35 36 const char *s1 = "fegfwe";37 const char *s2 = "Fwtfgt";38 const char *s3 = "geryhgr5";39 ::max(s1, s2, s3);40 41 42 return 0;43 }

 

OK , BUG 解决了。

 

 

思考:  

1.传值和传引用对于参数来说,本质区别在于是否产生了局部变量。

转载于:https://www.cnblogs.com/DLzhang/p/3998967.html

你可能感兴趣的文章
汇编笔记
查看>>
点击qq、点击邮箱01
查看>>
时间处理总结(三)javascript与WCF
查看>>
Ubantu下安装jdk 教程
查看>>
ActiveMQ入门实例
查看>>
linux安装至少有哪两个分区,各自作用是什么?
查看>>
swoole 安装和简单实用
查看>>
文件系统 第八次迭代 VFS相关说明
查看>>
速读《构建之法:现代软件工程》提问
查看>>
SpringCloud注册中心环境搭建euraka
查看>>
ElasticSearch 安装使用
查看>>
React性能分析利器来了,妈妈再也不用担心我的React应用慢了(转)
查看>>
信息安全管理(1):组织的三个层面
查看>>
原生JS实现圆周运动
查看>>
文件的读写
查看>>
前端面试通关指南
查看>>
制作首页的显示列表。
查看>>
同样加班 不同收获
查看>>
数据公钥加密和认证中的私钥公钥
查看>>
c语言中的位移位操作
查看>>