• 函数模板与模板函数


    1.函数指针——指针函数 

    函数指针的重点是指针。表示的是一个指针,它指向的是一个函数,例子: 

    int   (*pf)(); 

    指针函数的重点是函数。表示的是一个函数,它的返回值是指针。例子: 

    int*   fun(); 

    2.数组指针——指针数组 

    数组指针的重点是指针。表示的是一个指针,它指向的是一个数组,例子: 

    int   (*pa)[8]; 

    指针数组的重点是数组。表示的是一个数组,它包含的元素是指针。例子; 

    int*   ap[8]; 

    3.类模板——模板类(class   template——template   class) 

    类模板的重点是模板。表示的是一个模板,专门用于产生类的模子。例子: 

    template   <typename   T> 

    class   Vector 



                … 

    }; 

    使用这个Vector模板就可以产生很多的class(类),Vector <int> 、Vector <char> 、Vector <   Vector <int>   > 、Vector <Shape*> ……。 

    模板类的重点是类。表示的是由一个模板生成而来的类。例子: 

    上面的Vector <int> 、Vector <char> 、……全是模板类。 

    这两个词很容易混淆,我看到很多文章都将其用错,甚至一些英文文章也是这样。将他们区分开是很重要的,你也就可以理解为什么在定义模板的头文件.h时,模板的成员函数实现也必须写在头文件.h中,而不能像普通的类(class)那样,class的声明(declaration)写在.h文件中,class的定义(definition)写在.cpp文件中。

    array是一个模板,array<int, 50>是一个模板实例 - 一个类型。从array创建array<int, 50>的过程就是实例化过程。实例化要素体现在main.cpp文件中。如果按照传统方式,编译器在array.h文件中看到了模板的声明,但没有模板的定义,这样编译器就不能创建类型array<int, 50>。但这时并不出错,因为编译器认为模板定义在其它文件中,就把问题留给链接程序处理。

    现在,编译array.cpp时会发生什么问题呢?编译器可以解析模板定义并检查语法,但不能生成成员函数的代码。它无法生成代码,因为要生成代码,需要知道模板参数,即需要一个类型,而不是模板本身。

    这样,链接程序在main.cpp 或 array.cpp中都找不到array<int, 50>的定义,于是报出无定义成员的错误。


    关于一个缺省模板参数的例子: 

    template   <typename   T   =   int> 

    class   Array 



                … 

    }; 

    第一次我定义这个模板并使用它的时候,是这样用的: 

    Array   books;//我认为有缺省模板参数,这就相当于Array <int>   books 

    上面的用法是错误的,编译不会通过,原因是Array不是一个类。正确的用法是Array <>   books; 

    这里Array <> 就是一个用于缺省模板参数的类模板所生成的一个具体类。 

    4.函数模板——模板函数(function   template——template   function) 

    函数模板的重点是模板。表示的是一个模板,专门用来生产函数。例子: 

    template   <typename   T> 

    void   fun(T   a) 



                … 



    在运用的时候,可以显式(explicitly)生产模板函数,fun <int> 、fun <double> 、fun <Shape*> ……。 

    也可以在使用的过程中由编译器进行模板参数推导,帮你隐式(implicitly)生成。 

    fun(6);//隐式生成fun <int> 

    fun(8.9);//隐式生成fun <double> 

    fun(‘a’);//   隐式生成fun <char> 

    Shape*   ps   =   new   Cirlcle; 

    fun(ps);//隐式生成fun <Shape*> 

    模板函数的重点是函数。表示的是由一个模板生成而来的函数。例子: 

    上面显式(explicitly)或者隐式(implicitly)生成的fun <int> 、fun <Shape*> ……都是模板函数。 

    模板本身的使用是很受限制的,一般来说,它们就只是一个产生类和函数的模子。除此之外,运用的领域非常少了,所以不可能有什么模板指针存在的,即指向模板的指针,这是因为在C++中,模板就是一个代码的代码生产工具,在最终的代码中,根本就没有模板本身存在,只有模板具现出来的具体类和具体函数的代码存在。 

    提醒:在本文的几个术语中,语言的重心在后面,前面的词是作为形容词使用的。 

    2 函数模板的异常处理

    函数模板中的模板形参可实例化为各种类型,但当实例化模板形参的各模板实参之间不完全一致时,就可能发生错误,如:

    复制代码
    template<typename T>       
    void min(T &x, T &y)
    {  return (x<y)?x:y;  }
    
    void func(int i, char j)
    {
       min(i, i);
       min(j, j);
    
       min(i, j);   
       min(j, i);
    }
    复制代码

    例子中的后两个调用是错误的,出现错误的原因是,在调用时,编译器按最先遇到的实参的类型隐含地生成一个模板函数,并用它对所有模板函数进行一致性检查,例如对语句

    min(i, j);

    先遇到的实参i是整型的,编译器就将模板形参解释为整型,此后出现的模板实参j不能解释为整型而产生错误,模板函数是没有隐含的类型转换功能的。解决此种异常的方法有两种:

    ⑴采用强制类型转换,如将语句min(i, j);改写为min(i,int( j));

    ⑵用非模板函数重载函数模板

    方法有两种:

    ① 借用函数模板的函数体

    此时只声明非模板函数的原型,它的函数体借用函数模板的函数体。如改写上面的例子如下:

    复制代码
    template<typename T>       
    void min(T &x, T &y)
    {  return (x<y)?x:y;  }
    
    int min(int,int);
    
    void func(int i, char j)
    {
       min(i, i);
       min(j, j);
    
       min(i, j);
       min(j, i);
    }
    复制代码

    执行该程序就不会出错了,因为重载函数支持数据间的隐式类型转换。

    ② 重新定义函数体

    就像一般的重载函数一样,重新定义一个完整的非模板函数,它所带的参数可以随意。

    C++中,函数模板与同名的非模板函数重载时,应遵循下列调用原则:

    • 寻找一个参数完全匹配的函数,若找到就调用它。若参数完全匹配的函数多于一个,则这个调用是一个错误的调用。

    • 寻找一个函数模板,若找到就将其实例化生成一个匹配的模板函数并调用它。

    • 若上面两条都失败,则使用函数重载的方法,通过类型转换产生参数匹配,若找到就调用它。

    •若上面三条都失败,还没有找都匹配的函数,则这个调用是一个错误的调用。

    3.函数模板与类模板有什么区别?

    答:函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化必须由程序员在程序中显式地指定。

         即函数模板允许隐式调用和显式调用而类模板只能显示调用

  • 相关阅读:
    静态代理模式
    反射+抽象工厂
    抽象工厂模式
    工厂方法模式
    简单工厂模式
    单例模式
    博客总览
    Bootstrap快速上手 --作品展示站点
    Java 网络编程---分布式文件协同编辑器设计与实现
    如何在博客园的博客中添加可运行的JS(转载)
  • 原文地址:https://www.cnblogs.com/zhaodun/p/6400934.html
Copyright © 2020-2023  润新知