zoukankan      html  css  js  c++  java
  • C++中函数模板的深入理解

    1,函数模板深入理解:

        1,编译器从函数模板通过具体类型产生不同的函数;

           1,模板就是模子,通过这个模子可以产生很多的实物;

           2,函数模板就是编译器用来产生具体函数的模子;

        2,编译器会对函数模板进行两次编译:

           1,对模板代码本身进行编译;

               1,检查函数模板本身有无语法错误等;

           2,对参数替换后的代码进行编译;

               1,调用函数模板的时候,编译器要根据实际的参数类型得到真正的函数,这个时候编译器会对这个函数进行第二次编译;

          

    2,注意事项:

        1,函数模板本身不允许隐式类型转换:

           1,自动推导类型时,必须严格匹配;

               1,函数模板本身不是函数,它只是产生函数的模子,因此函数模板本身不允许隐式类型转换;

           2,显示类型指定时,能够进行隐式类型转换;

               1,显示指定类型,就会得到真实的函数,这个时候有可能进行隐式类型转换;

                     

    3,函数模板的本质编程实验:

     1 #include <iostream>
     2 #include <string>
     3 
     4 using namespace std;
     5 
     6 class Test
     7 {
     8     Test(const Test&);
     9 public:
    10     Test()
    11     {
    12     }
    13 };
    14 
    15 template < typename T >
    16 void Swap(T& a, T& b)  // 这里编译器显示为: In function 'void Swap(T&, T&)';说明这也是函数,是模板函数;
    17 {
    18     T c = a;
    19     // a = b  // 当文件中只有类模板一个参数时,编译器显示:error: expected ';' before 'b';证明了编译器还是会对模板本生进行编译;
    20     a = b;  
    21     b = c;
    22 }
    23 
    24 typedef void(FuncI)(int&, int&);  // 定义函数类型,类型名为 FuncI; 
    25 typedef void(FuncD)(double&, double&);
    26 typedef void(FuncT)(Test&, Test&);
    27 
    28 int main()
    29 {
    30     /* pi 和 pd 指向具体函数,但是 pi 和 pd 指向的具体函数是由函数模板具体产生,这里已经产生了函数,不是模板了,这两个函数是两个独立的不同的函数 */
    31     FuncI* pi = Swap;    // 定义函数指针,并能编译通过;这里编译器通过函数指针的类型自动推导 T 为 int; 编译器默默做的事有:1,首先进行自动推导;2,产生真正的 Swap() 函数;3,将实际生成的 Swap() 函数地址用于初始化 pi;
    32 
    33     FuncD* pd = Swap;    // 编译器自动推导 T 为 double;
    34     
    35     /* 证明编译器做了第二次编译 */
    36     FuncT* pt = Swap;    // 没有显示声明拷贝构造函数时,编译器自动推导 T 为 Test;
    37     // FuncT* pt = Swap;  // 编译器显示:instantiated from here  
    38                           //  error: 'Test::Test(const Test&)' is private
    39                           //  error: within this context(也就是 Swap 中 T c = a; 语句);
    40                           // 这里的编译错误是二次编译时发现的,自动推导 T 为 Test 后,会产生一个 Swap() 函数,接下来会对 Swap() 函数进行编译,接下来就发现 T c = a; 行有问题;
    41     
    42     cout << "pi = " << reinterpret_cast<void*>(pi) << endl;   // 将 pi 这个指针的类型重解释为 void*;打印:0x80487a8;
    43     cout << "pd = " << reinterpret_cast<void*>(pd) << endl;  // 打印:0x80487ca;
    44     cout << "pt = " << reinterpret_cast<void*>(pt) << endl;  // 没有显示声明拷贝构造函数时,打印:0x8048828;
    45     // cout << "pt = " << reinterpret_cast<void*>(pt) << endl;  
    46     
    47     return 0;
    48 }

       

    4,多参数函数模板:

        1,函数模板可以定义任意多个不同的类型参数:

           1,代码示例:

    1 template < typename T1, typename T2, typename T3 >
    2 T1 Add(T2 a, T3 b)
    3 {
    4     return static_cast<T1>(a + b);
    5 }
    6            
    7 int r = Add<int, float, double>(0.5, 0.8);

           2,仅仅是调用的时候多了几个泛指类型而已;

        2,对于多参数函数模板:

           1,无法自动推导返回值类型;

               1,只有返回值类型必须手工的指定;

               2,一般的工程中指定第一个模板参数为返回值类型;

           2,可以从左向右部分指定函数类型参数:

     

               1,剩下的参数类型编译器可以自动推导;

           3,工程中,将返回值参数作为第一个类型参数;

               1,返回值是泛指类型,则将第一个类型参数作为返回值类型;

               2,这是工程中手动设置的,并不是编译器自己的行为;

          

    5,多参数函数模板编程实验:

     1 #include <iostream>
     2 #include <string>
     3 
     4 using namespace std;
     5 
     6 template 
     7 < typename T1, typename T2, typename T3 >
     8 T1 Add(T2 a, T3 b)
     9 {
    10     return static_cast<T1>(a + b);
    11 }
    12 
    13 
    14 int main()
    15 {
    16     // T1 = int, T2 = double, T3 = double
    17     int r1 = Add<int>(0.5, 0.8);
    18 
    19     // T1 = double, T2 = float, T3 = double
    20     double r2 = Add<double, float>(0.5, 0.8);
    21 
    22     // T1 = float, T2 = float, T3 = float
    23     float r3 = Add<float, float, float>(0.5, 0.8);
    24 
    25     cout << "r1 = " << r1 << endl;     // r1 = 1
    26     cout << "r2 = " << r2 << endl;     // r2 = 1.3
    27     cout << "r3 = " << r3 << endl;     // r3 = 1.3
    28     
    29     return 0;
    30 }

       

    6,当函数重载遇见函数模板会发生什么?

        1,见下本文7 中的分析;

     

    7,重载函数模板:

        1,函数模板可以像普通函数一样被重载:

           1,C++ 编译器优先考虑普通函数;

           2,如果函数模板可以产生一个更好的匹配,那么选择模板;

           3,可以通过空模板实参列表限定编译器只匹配模板:

     

              

    8,重载函数模板实例分析:

     1 #include <iostream>
     2 #include <string>
     3 
     4 using namespace std;
     5 
     6 template < typename T >
     7 T Max(T a, T b)
     8 {
     9     cout << "T Max(T a, T b)" << endl;
    10     
    11     return a > b ? a : b;
    12 }
    13 
    14 int Max(int a, int b)  // C++ 中这个函数可以重载上面的模板;
    15 {
    16     cout << "int Max(int a, int b)" << endl;
    17     
    18     return a > b ? a : b;
    19 }
    20 
    21 template < typename T >
    22 T Max(T a, T b, T c)
    23 {
    24     cout << "T Max(T a, T b, T c)" << endl;
    25     
    26     return Max(Max(a, b), c);  // 这里是重载的模板;
    27 }
    28 
    29 int main()
    30 {
    31     int a = 1;
    32     int b = 2;
    33     
    34     cout << Max(a, b) << endl;   // 普通函数 Max(int, int)
    35     
    36     cout << Max<>(a, b) << endl;  // 函数模板 Max<int>(int, int)
    37     
    38     cout << Max(3.0, 4.0) << endl;  // 函数模板 Max<double>(double,double)
    39     
    40     cout << Max(5.0, 6.0, 7.0) << endl;  // 函数模板 Max<double>(double, double, double)
    41     
    42     cout << Max('a', 100) << endl;    // 普通函数 Max(int, int);函数模板是不会进行隐式类型转换的,因此这时不会考虑上述两个模板进行隐式推导,这里直接考虑普通函数并进行隐式类型转换;
    43     
    44     return 0;
    45 }

       

    9,小结:

        1,函数模板通过具体类型产生不同的函数;

        2,函数模板可以定义任意多个不同的类型参数;

        3,函数模板中的返回值类型必须显示指定;

        4,函数模板可以像普通函数一样被重载;

  • 相关阅读:
    标准粒子群算法(PSO)
    Java开发中的23种设计模式详解
    分布式事务
    sjk 分页
    有用吗2
    有用吗1
    存储过程
    在虚拟机Linux安装Redis
    ajax调用WebAPI添加数据
    SVN安装和使用(简单版)
  • 原文地址:https://www.cnblogs.com/dishengAndziyu/p/10918622.html
Copyright © 2011-2022 走看看