C++学习之模板(一) ----函数模板
本博文主要讨论函数模板及其简单应用。
1)、作用:函数模板可以看做是一种代码产生器,往里面放入具体的类型,得到具体化的函数。
2)、编译(分为两步):
a):实例化之前,先检查模板本身语法是否正确;
b):根据 函数调用调用 ,先去实例化模板代码,产生具体的函数。
也就是说, 没有函数调用,就不会实例化模板代码,在目标文件obj中找不到模板的痕迹。
3):优缺点
模板的缺点是代码膨胀,编译速度慢,而优点是运行速度快。
一、函数模板:
1)、简单操作,代码实现如下:
1 #include <iostream> 2 #include <string> 3 #include <vector> 4 using namespace std; 5 6 template <typename T> 7 //修改办法2: 8 //T max(const T &a, const T &b) 9 const T &max(const T &a, const T &b) 10 { 11 return a > b ? a: b; 12 } 13 14 int main(int argc, const char *argv[]) 15 { 16 cout << ::max(7, 42) << endl; //ok 17 18 cout << ::max(5.5, 7.8) << endl; //ok 19 20 string s1 = "hello"; 21 string s2 = "world"; 22 cout <<::max(s1, s2)<< endl; //ok 23 24 //cout << ::max("hello", "worldd");//error char[6],char[7] 25 //虽然都是字符数组,但是长度也是参数的一部分,故两者不是同一类型 26 27 //cout << ::max(3, 4.5) << endl;//error 28 //会发生编译错误,因为编译器推断第一个类型为int,第二个为double,没有一个模板符合这个要求,这样就会发生强制转换而产生一个局部的中间变量。这个变量的引用作为return的返回值;即此时我们引用了一个局部变量,这时会产生错误. 29 //修改办法1: 30 cout <<::max<int>(3, 6.5) << endl; 31 cout << ::max(3, static_cast<int>(6.5)) << endl; 32 33 return 0; 34 }
2)、一个非模板函数可以和一个同名的函数模板同时存在;两者可以因为参数不同而构成重载;
模板函数重载时,现则函数版本的一些特点:
a):条件相同时,选择非模板函数;例如 ::max(7,42);
b):在强制类型转化,与可行的实例化模板之间,优先选择实例化模板;例如::max(7.0,43.5),::max(‘a’,‘b’);
c):若实例化版本不可行,则尝试普通函数的转化,例如::max(‘a’。42.7)
d)参数是指针时,优先选择可实例化模板的引用版本,若不存在,则优先选择指针版本;
e):总之,尽可能采用最匹配、开销最小的版本。
示例代码及注释如下:
1 #include <iostream> 2 #include <string> 3 #include <vector> 4 using namespace std; 5 6 //调用策略--->精准调用 7 const int &max(const int &a, const int &b) 8 { 9 cout << "num.1"<< endl; 10 return a> b? a: b; 11 } 12 13 template <typename T> 14 const T *max(const T *a, const T *b) 15 { 16 cout <<"num.4" <<endl; 17 return *a > *b ? a:b ; 18 } 19 20 template <typename T> 21 const T &max(const T &a,const T &b) 22 { 23 cout << "num.2" << endl; 24 return a> b? a: b; 25 } 26 27 template <typename T> 28 const T &max(const T &a, const T &b, const T &c) 29 { 30 cout << "num.3"<< endl; 31 return ::max(::max(a, b), c); 32 } 33 34 int main(int argc, const char *argv[]) 35 { 36 cout <<::max(7, 42, 68) << endl; //3 1 1 37 cout <<::max(7.0, 43.6) << endl; //2 38 cout <<::max('a','b') << endl;//2 char 39 cout <<::max(7, 42) << endl;//2 40 cout <<::max<>(7,42) << endl; //2 特定模板 41 cout << ::max<double>(7,42)<< endl;//2 42 cout <<::max('a', 43.7)<<endl; //1 强制转换 43 44 int a = 7; 45 int b = 89; 46 int *p1 = &a; 47 int *p2 = &b; 48 cout << ::max(p1, p2) << endl;//2 传引用,减少开销 49 50 return 0; 51 }
3)、值传递与引用传递的区别:
a):值传递与引用传递对于形参而言,本质区别在于是否产生了局部变量;
b):对于返回值而言,其区别在于, 返回时 是否产生了 临时变量。
c): 对于非引用类型 的参数, 在实参演绎的过程中,会出现 从数组衰退成指针(decay),从而丢失长度信息;而引用类型 则不会引发 衰退 decay。
在模板函数重载中, 不要混合使用值传递和引用传递,而且要尽可能使用引用。
示例代码及注释如下:
1 #include <iostream> 2 #include <string> 3 #include <vector> 4 #include <string.h> 5 using namespace std; 6 7 //传引用 8 template <typename T> 9 const T &max(const T &a, const T &b) 10 { 11 cout <<"num.1" << endl; 12 return a> b? a: b; 13 } 14 15 //传值 16 //修改1-->改为传引用 17 //const char *&max(const char *&a, const char *&b) 18 const char *max(const char *a, const char *b) 19 { 20 cout <<"num.2" << endl; 21 return ::strcmp(a, b)> 0? a: b; 22 } 23 24 //传引用 25 //修改2-->改为传值 26 //const T max(const T &a, const T &b, const T &c) 27 template <typename T> 28 const T &max(const T &a, const T &b, const T &c) 29 { 30 cout <<"num.3" << endl; 31 return ::max(::max(a, b), c);//return 临时变量 32 //这里将临时变量的引用返回出去,可能导致错误 33 //const char*tmp=::max(::max(s1,s2),s3) 34 } 35 36 int main(int argc, const char *argv[]) 37 { 38 cout <<::max(7, 42, 68) << endl; 39 40 const char *s1 = "beij"; 41 const char *s2 = "shangh"; 42 const char *s3 = "shenzh"; 43 44 cout <<::max(s1, s2, s3) << endl; 45 46 return 0; 47 }
decay衰退的简单示例:
1 #include <iostream> 2 #include <string> 3 #include <vector> 4 using namespace std; 5 6 template <typename T> 7 T max(T a, T b) //值传递-->退化成指针,返回得到是两个指针地址的较大者 8 { 9 return a > b ? a : b; 10 } 11 12 template <typename T> 13 const T &max(const T &a, const T &b) //传引用 14 { 15 return a > b ? a : b; 16 } 17 18 int main(int argc, const char *argv[]) 19 { 20 string s = "hello"; 21 22 ::max("hello", "world"); 23 ::max("apple", "orange");//error 24 ::max("apple", s); 25 26 return 0; 27 }