zoukankan      html  css  js  c++  java
  • 函数模板

    本节知识点:

    1.泛型编程:

       a.泛型编程,即不考虑具体数据类型的编程模式

    2.函数模板(c++中的泛型编程):

       a.函数模板,即提供一种特殊的函数可用不同类型进行调用,主要区别是类型可被参数化!
       b.函数模板的语法规则:
       c.函数模板的应用(有两种,一种是编译器自动推导调用,另一种是具体类型显示调用):
    示例代码:
    1. #include <cstdlib>  
    2. #include <iostream>  
    3.   
    4. using namespace std;  
    5.   
    6. template<typename T> //定义为函数模板  泛型为T表示   
    7. void swap1(T& a, T& b)  
    8. {  
    9.     T c = a;  
    10.     a = b;  
    11.     b = c;  
    12. }  
    13.   
    14. int main()  
    15. {  
    16.     int a = 12;  
    17.     int b = 10;  
    18.     cout << a << " " << b << endl;  
    19.     swap1(a,b); //自动类型推导调用   
    20.     cout << a << " " << b << endl;   
    21.       
    22.     float a1 = 120;  
    23.     float b1 = 100;  
    24.     cout << a1 << " " << b1 << endl;  
    25.     swap1<float>(a1,b1); //具体类型显示调用   
    26.     cout << a1 << " " << b1 << endl;   
    27.     return 0;  
    28. }   

    3.函数模板的深入理解:

       a.编译器并不是把函数模板处理成能够处理任意类型的函数,编译器从函数模板中通过具体类型产生不同的函数。
       b.编译器会对函数模板进行两次编译,第一次是在声明的地方对函数模板代码本身进行编译,查看是否有语法错误。第二次是在模板函数被调用的地方对参数替换后的代码进行编译。
       c.使用函数模板的过程是,首先声明并定义函数模板,当调用函数模板的时候,编译器自己或程序员显示获得模板中参数的类型。当获得参数类型后,编译器会根据参数类型和函数模板产生一个具体参数类型的函数,并调用这个函数,而不是调用函数模板(这个过程可以参看程序的汇编代码,可知调用了那种参数类型的函数模板,就会产生那种参数类型的具体函数,没调用的类型则没有)。即void swap1(T& a, T& b)这个函数模板,可适合char、int、double等类型。如果不调用swap1这个函数,函数模板则不会被编译进程序成为最终的二进制文件,不占用空间。如果只这样swap1<int>(a, b)调用函数,最终的二进制文件中只有一个void swap1(int& a, int& b)这样的函数,没有其他类型的函数。

    4.函数模板的特化:

      a.函数模板特化的语法规则:
         第一种:
    1. template <>  
    2. void swap1(float& a, float& b)  
         第二种:
    1. template <>  
    2. void swap1<float>(float& a, float& b)  
       b.函数模板特化的应用:在一些情况不能完全使用泛型编程的时候(即函数模板不适合所有数据类型的时候),就需要使用特化把特殊情况,单独写成函数。如上面代码,当swap1函数的参数是float型的时候,就不再根据原先的函数模板生成具体的函数了,直接调用特化的函数。
       c.函数模板仅仅是起到一个模板的作用,是帮助根据调用类型生成具体类型函数的,函数模板本身是不会被编译进入二进制文件的(可执行文件)。事实证明也是如此,通过对汇编代码的观察,发现汇编代码中是没有函数模板的对应汇编代码的,只有被调用后产生了具体函数,才会有具体的汇编代码。所以函数模板是不会存在在二进制文件中的。但是函数模板的特化,其实就是已经产生了具体类型的函数了,所以有对应的汇编代码,也会存在在二进制文件中。
    代码示例:
    1. #include <iostream>  
    2.   
    3. using namespace std;  
    4.   
    5. template <typename T>  
    6. void swap1(T& a, T& b)  
    7. {  
    8.     T c = a;  
    9.     a = b;  
    10.     b = c;  
    11. }  
    12. template <>  
    13. void swap1(float& a, float& b)  //等效void swap1<float>(float& a, float& b)   
    14. {  
    15.     float  c = a;  
    16.     a = b;  
    17.     b = c;  
    18.     cout << "hello float swap1" << endl;  
    19. }   
    20. int main()  
    21. {  
    22.     int a = 100;  
    23.     int b = 120;  
    24.     cout << a << " " << b << endl;   
    25.     swap1(a, b);  
    26.     cout << a << " " << b << endl;  
    27.       
    28.     float fa = 1.1;  
    29.     float fb = 1.2;  
    30.     cout << fa << " " << fb << endl;   
    31.     swap1(fa, fb);  
    32.     cout << fa << " " << fb << endl;  
    33.     return 0;  
    34. }  

    5.函数模板与重载:

       a.函数模板之间也是可以发生函数重载的
       b.c++编译器优先考虑普通函数
       c.如果函数模板可以产生一个更好的匹配,那么选择使用模板
       d.可以通过空模板实参列表的语法限定编译器只通过模板匹配
    示例代码:
    1. #include <iostream>  
    2.   
    3. using namespace std;  
    4.   
    5. void swap1(int& a, int& b) //与函数模板是重载关系   
    6. {  
    7.     int c = a;  
    8.     a = b;  
    9.     b = c;  
    10.     cout << "swap1 int" << endl;  
    11. }  
    12.   
    13. template <typename T>  
    14. void swap1(T& a, T& b)  
    15. {  
    16.     T& c = a;  
    17.     a = b;  
    18.     b = c;  
    19.     cout << "swap1 Ta Tb" << endl;  
    20. }  
    21. template <typename T> //与函数模板重载关系   
    22. void swap1(T& a, T& b, T& d)  
    23. {  
    24.     T& c = a;  
    25.     a = b;  
    26.     b = c;  
    27.     cout << "swap1 Ta Tb Td" << endl;  
    28. }  
    29. int main()  
    30. {  
    31.     int a = 100;  
    32.     int b = 120;  
    33.     swap1(a,b); //对于模板与具体函数重载 编译器默认调用具体函数   
    34.     swap1<int>(a,b); //可以直接指明使用函数模板    说明类型   
    35.     swap1<>(a,b); //可以直接指明使用函数模板    编译器自动检测类型   
    36.       
    37.     float fa = 1.0;  
    38.     float fb = 1.2;  
    39.     swap1(fa,fb); //具体函数实现不了  使用函数模板进行匹配   
    40.       
    41.     char ca = 'a';  
    42.     char cb = 'b';  
    43.     char cc = 'c';  
    44.     swap1(ca, cb, cc); //函数模板之间也是可以发生重载的   
    45. }  

    6.多参数函数模板:

       a.对于T max(T a, T b)这样的模板,T只能是同一个类型,如果max(fa, b)调用,其中fa和b不是同一个类型,就不会使用max的函数模板,有模板重载的函数就会调用重载函数,没有就会报错,注意强制使用模板也会报错!还有就是对于函数模板的特化,如果特化的函数与模板函数不是一个类型的,即T的位置上,不是同一个类型的,也会报错的!代码如下:
    1. #include <iostream>  
    2.   
    3. using namespace std;  
    4.   
    5. int max(int a, int b)  
    6. {  
    7.     cout << "max int" << endl;  
    8.     return a>b?a:b;  
    9. }  
    10.   
    11. template <typename T>  
    12. T max(T a, T b)  
    13. {  
    14.     cout << "max T" << endl;  
    15.     return a>b?a:b;  
    16. }  
    17. int main()  
    18. {  
    19.     int a = 12;  
    20.     int c = 10;  
    21.     float b = 1.0;  
    22.     cout << max(a,b) << endl;  
    23. //  cout << max<>(a,b) << endl;  //这条语句是不可以 强制使用模板的 因为模板中T必须是一个类型   
    24.     return 0;  
    25. }  
    注意:函数模板不允许自动类型转换(即typename都是一致的),普通函数能够进行自动类型转换
       b.多参数函数模板:函数模板可以定义任意多个不同的类型参数,这样就有效的解决了上面的问题,代码如下:
    1. #include <iostream>  
    2.   
    3. using namespace std;  
    4.   
    5. template <typename T1, typename T2, typename Ri>  
    6. Ri add(T1 a, T2 b)  
    7. {  
    8.     return static_cast<Ri>(a + b);  
    9. }  
    10.   
    11. int main()  
    12. {  
    13.     int a = 12;  
    14.     float b = 2.98;  
    15.     cout << add<int,float,double>(a, b) << endl;  
    16.     return 0;  
    17. }  
         c.切记,在函数模板中声明了类型参数为返回值类型时(即Ri add(T1 a, T2 b)中的Ri就是),编译器就无法进行自动类型推导了。这里有一个解决办法,就是将返回类型参数声明到第一个参数位置,调用时只需显示声明返回类型参数即可。代码如下:
    1. #include <iostream>  
    2.   
    3. using namespace std;  
    4.   
    5. template <typename Ri, typename T1, typename T2> //注意Ri的位置   
    6. Ri add(T1 a, T2 b)  
    7. {  
    8.     return static_cast<Ri>(a + b);  
    9. }  
    10.   
    11. int main()  
    12. {  
    13.     int a = 12;  
    14.     float b = 2.98;  
    15.     cout << add<double>(a, b) << endl; //因为Ri为第一个参数位置     
    16.     return 0;  
    17. }  
  • 相关阅读:
    java字节中的基本类型的职业的数目 (采访总是问)
    hdu 1814 Peaceful Commission (2-sat 输出字典序最小的路径)
    Ubuntu Server 14.04 LTS(64bit)已安装 weblogic Server 12c(12.1.3) Zip Distribution
    Tyvj P1015 公路骑 (DP)
    编程算法
    POJ 2502 Subway (Dijkstra 最短+建设规划)
    android_Activity生命周期功能
    ftk学习记录(脚本文章)
    2013年周二
    2013年第32周星期1
  • 原文地址:https://www.cnblogs.com/haichun/p/3519244.html
Copyright © 2011-2022 走看看