zoukankan      html  css  js  c++  java
  • C++学习之路: 模板函数

     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 
    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 <iostream>
    #include <string>
    #include <vector>
    using namespace std;
    
    template <typename T>
    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 <iostream>
     2 #include <string>
     3 #include <vector>
     4 using namespace std;
     5 
     6 template <typename T>
     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 <iostream>
     2 #include <string>
     3 #include <vector>
     4 using namespace std;
     5 
     6 template <typename T1, typename T2>
     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 double
    18     // 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 <iostream>
     2 #include <string>
     3 #include <vector>
     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 <typename T>
    14 
    15 const T &max(const T &a, const T &b)
    16 {
    17     return a > b ? a: b ;
    18 }
    19 
    20 template <typename T>
    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<double>(7,42) <<endl;  // 调用2 进行强制转换。
    37     cout << ::max('a', 42.7) << endl;   //调用1, 进行强制转换
    38     return 0;
    39 }

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

     1 #include <iostream>
     2 #include <string>
     3 #include <vector>
     4 using namespace std;
     5 
     6 
     7 
     8 template <typename T>
     9 const T &max(const T &a, const T &b)
    10 {
    11     return a > b ? a : b ;
    12 }
    13 
    14 template <typename T>
    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 <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     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 <typename T>
    19 const T &max(const T &a, const T &b, const T &c)  //三参数版本的模板函数, 要根据情况来调用 上面两个
    20 {
    21     return ::max(::max(a, b), c) ;  //调用1 或者 2
    22 }
    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 <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     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 <typename T>
    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.传值和传引用对于参数来说,本质区别在于是否产生了局部变量。

  • 相关阅读:
    SharePoint 2013 配置基于表单的身份认证
    SharePoint 2013 场解决方案包含第三方程序集
    SharePoint 2010 站点附加数据升级到SP2013
    SharePoint 2013 在母版页中插入WebPart
    SharePoint 2013 搭建负载均衡(NLB)
    SharePoint 部署解决方案Feature ID冲突
    SharePoint 2013 配置基于AD的Form认证
    SharePoint Server 2016 Update
    SharePoint 2013 为用户组自定义EventReceiver
    SharePoint 2013 JavaScript API 记录
  • 原文地址:https://www.cnblogs.com/DLzhang/p/3998967.html
Copyright © 2011-2022 走看看