Step By Step(C++模板函数)
一、模板函数实例化:
存在这样一种函数,它们在行为上是完全一致的,而不同只是函数的参数类型。对于这种类型的函数,我们可以应用C++模板和泛型来帮助我们更优雅和更具技巧性的的解决一些程序设计上的实际问题。如:
1 template <typename T> 2 inline T const& max(T const& a, T const& b) { 3 return a < b ? b : a; 4 }
对于上面的模板函数,我们在实际的应用中可以用任意类型来实例化该模板函数,如:
1 int main() { 2 printf("The max value is %d\n", ::max(4,5)); //int max(int, int) 3 printf("The max value is %f\n", ::max(4.2,5.3)); //float max(float, float) 4 return 0; 5 }
在上面的实例中,C++编译器会通过调用参数将模板函数实例化,同时返回比较后的结果。当然,对于这样的方法我们也可以通过宏的方式来实现,如:
#define max(a,b) ((a) < (b)) ? (b) : (a)
相比于模板函数,宏的方式还是存在一些问题的,比如类型不安全。由于这个系列主要介绍的是C++模板,因此对于其他技术就不做更多的赘述了。对于模板函数max,有一些几点需要注意:
1. 参数a和参数b的类型要保持一致,否则编译器无法进行正确的类型推演。如:int a = max(4,3.3)。对于这种调用情况,我们可以修改为int a = max(4, (int)3.3),或者修改为int a = max<int>(4,3.3),即显示的指定模板函数max的类型参数。
2. 对于类型T的实际类型而言,该类型必需提供operator <() 的支持,否则无法成功编译。对于上面的示例,整型和浮点型均提供了小于操作符的内嵌支持,而对于其它的class而言,则需要给class提供重载小于操作符的方法。如:
1 class MyClass { 2 public: 3 MyClass(int value) : _value(value) { 4 } 5 bool operator< (const MyClass& other) const { //注意这里的操作符重载方法必需是const函数。 6 printf("operator < is call\n"); 7 return _value < other._value; 8 } 9 int myValue() const { return _value; } 10 private: 11 int _value; 12 }; 13 14 int main() 15 { 16 MyClass m1(30), m2(40); 17 printf("The max value is %d\n",::max(m1,m2).myValue()); 18 return 0; 19 }
如果MyClass类没有提供bool operator< (const MyClass& other) const函数,该段代码将无法成功编译。
3. 函数参数传递的是引用,而不是传值。这样便可以在函数声明时返回应用类型。否则,如果函数参数是传值的,如:T const& max(T const a, T const b),那么对于该种情况,由于在比较时将会使用函数内部的两个临时变量,即实数a和b在函数内的副本。这样在函数返回时,如果返回临时变量的引用后果可想而知。因此可改为:T const max(T const a, T const b)。
4. C++的编译器会为每一种类型都产生一个实例化函数,而不是仅仅构造出一个函数实例,然后再基于不同的参数类型调用该通用函数。如:
1 template <typename T> 2 inline T const& max(T const& a, T const& b) { 3 static int i = 0; 4 printf("The value of i is %d\n",i++); 5 return a < b ? b : a; 6 } 7 8 class MyClass { 9 public: 10 MyClass(int value) : _value(value) { 11 } 12 bool operator< (const MyClass& other) const { 13 return _value < other._value; 14 } 15 int myValue() const { return _value; } 16 private: 17 int _value; 18 }; 19 20 int main() 21 { 22 ::max(4,5); //for max<int> 23 ::max(5,6); //for max<int> 24 ::max(5.3,5.4); //for max<float> 25 ::max(5.3,5.4); //for max<float> 26 MyClass m1(30), m2(40); 27 ::max(m1,m2); //for max<MyClass> 28 getch(); 29 return 0; 30 } 31 //The value of i is 0 32 //The value of i is 1 33 //The value of i is 0 34 //The value of i is 1 35 //The value of i is 0
模板函数max中的变量i是静态变量,该变量只会在函数第一次被调用时初始化一次。从输出结果可以看出,该变量被初始化三次,每一次都是基于不同类型的模板函数实例化。由此可见,max模板函数确实在代码段中被生成三份。
二、重载函数模板:
和普通的函数重载一样,模板函数也可以实现函数重载的功能。如:
1 #include <stdio.h> 2 #include <conio.h> 3 4 inline int const& max(int const& a, int const& b) { 5 printf("int const& max(int const& a, int const& b) is called.\n"); 6 return a < b ? b : a; 7 } 8 9 template <typename T> 10 inline T const& max(T const& a, T const& b) { 11 printf("T const& max(T const& a, T const& b) is called.\n"); 12 return a < b ? b : a; 13 } 14 15 template <typename T> 16 inline T const& max(T const& a, T const& b, T const& c) { 17 printf("T const& max(T const& a, T const& b, T const& c) is called.\n"); 18 return ::max(::max(a,b),c); 19 } 20 21 class MyClass { 22 public: 23 MyClass(int value) : _value(value) { 24 } 25 bool operator< (const MyClass& other) const { 26 printf("operator < is call\n"); 27 return _value < other._value; 28 } 29 int myValue() const { return _value; } 30 private: 31 int _value; 32 }; 33 34 int main() { 35 ::max(5,43,68); //三个参数的max<int> 36 printf("------------------------------------\n"); 37 ::max('a','b'); //两个参数的max<char> 38 ::max(7,54); //int参数类型的非模板函数。 39 ::max<>(7,54); //两个参数的max<int> 40 ::max('a',43.2); //int参数类型的非模板函数。 41 getch(); 42 return 0; 43 } 44 // T const& max(T const& a, T const& b, T const& c) is called. 45 // int const& max(int const& a, int const& b) is called. 46 // int const& max(int const& a, int const& b) is called. 47 // ------------------------------------ 48 // T const& max(T const& a, T const& b) is called. 49 // int const& max(int const& a, int const& b) is called. 50 // T const& max(T const& a, T const& b) is called. 51 // int const& max(int const& a, int const& b) is called.
从上面的输出可以看出,可以了解到以下几点:
1. 非模板函数和模板函数可以同名,在调用时可以一起进行函数重载的推演。
2. 编译器在进行函数重载的推演时,首选非模板类型的函数。
3. 通过max<>方式通知编译器,在推演时只考虑模板函数。
4. 由于模板函数在实例化的过程中,不会自动完成任何形式的类型转换,因此对于最后一个调用函数,由于两个参数的类型不一致,而普通函数max通过类型自动转换((int)'a',(int)43.2)可以覆盖该调用,所以编译器选择了该普通函数。