C++ Primer 学习笔记_86_模板与泛型编程 --重载与函数模板

2014-11-24 13:23:15 · 作者: · 浏览: 25

模板与泛型编程

--重载与函数模板

引言:

函数模板可以重载:可以定义有相同名字但参数数目或类型不同的多个函数模板,也可以定义与函数模板有相同名字的普通非模板函数。

但是,声明一组重载函数模板不保证可以成功调用它们,重载的函数模板可能会导致二义性。



一、函数匹配与函数模板

如果重载函数中既有普通函数又有函数模板,确定函数调用的步骤如下:

1.为这个函数名建立候选函数集合,包括:

a.与被调用函数名字相同的任意普通函数。

b.任意函数模板实例化,在其中,模板实参推断发现了与调用中所用函数实参相匹配的模板实参

2.确定哪些普通函数是可行的(如果有可行函数的话)。候选集合中的每个模板实例都可行的,因为模板实参推断保证函数可以被调用。

3.如果需要转换来进行调用,根据转换的种类排列可靠函数,记住,调用模板函数实例所允许的转换是有限的

a.如果只有一个函数可选,就调用这个函数。

b.如果调用有二义性,从可行函数集合中去掉所有函数模板实例。

4.重新排列去掉函数模板实例的可行函数。

a.如果只有一个函数可选,就调用这个函数。

b.否则,调用有二义性。



二、函数模板匹配的例子

template 
  
   
int compare(const T &,const T &);

template 
   
     int compare(U,U,V); int compare(const char *,const char *); 
   
  

重载集合包含三个函数:第一个模板处理简单值,第二个模板比较两个序列的元素,第三个是处理C风格字符串的普通函数。



三、确定重载函数模板的调用

可以在不同类型上调用这些函数:

    compare(1,0);

    vector
  
    ivec1(10),ivec2(20);
    compare(ivec1.begin(),ivec1.end(),ivec2.begin());

    int ia1[] = {0,1,2,3,4,5,6,7,8,9};
    compare(ia1,ia1 + 10,ivec1.begin());

    const char const_arr1[] = "world",const_arr2[] = "hi";
    compare(const_arr1,const_arr2);

    char ch_arr1[] = "world",ch_arr2[] = "hi";
    compare(ch_arr1,ch_arr2);

  

compare(1,0):

两个形参都是int类型。候选函数是第一个模板将T绑定到 int的实例 化,以及名为compare的普通函数。但该普通函数不可行―― 不能将 int对象传给期待char*对象的形参。用int实例化的函数与该调用完全匹配,所以选择它。


compare(ivec1.begin(),ivec1.end(), ivec2.begin()) :

compare(ia1,ia1+ 10,ivec1.begin()):

这两个调用中,唯一可行的函数是有三个形参的模板的实例化。带两个参数的模板和普通非模板函数都不能匹配这两个调用。


compare(const_arr1,const_arr2:

这个调用正如我们所期待的,调用普通函数。该函数和将T绑定到 constchar* 的第一个模板都是可行的,也都完全匹配。根据规则(3)b,会选择普通函数。从候选集合中去掉模板实例,只剩下普通函数可行。


compare(ch_arr1,ch_arr2):

这个调用也绑定到普通函数。候选者是将T绑定到 char*的函数模板的版本,以及接受constchar* 实参的普通函数,两个函数都需要稍加转换将数组ch_arr1ch_arr2转换为指针。因为两个函数一样匹配,所以普通函数优先于模板版本。



四、转换与重载的函数模板

设计一组重载函数,其中一些是模板而另一些是普通函数,这可能是困难的。这样做需要深入理解类型之间的关系,具体而言,就是当设计模板时,可能发生和不能发生的隐式转换。如:

    char *ch_arr1 = "world",*ch_arr2 = "hi";
    compare(ch_arr1,ch_arr2);

这个调用模板版本匹配!通常,我们希望无论是传递数组,还是传递指向该数组元素的指针,都获得同一函数。但是,在这个例子中,将char*绑定到 T的函数模板与该调用完全匹配。普通版本仍然需要从char*到 constchar * 的转换,所以优先选择函数模板。

另一个具有惊人结果的改变是,如果compare的模板版本有一个T类型的形参代替T的 const引用,会发生的情况:

template 
  
   
int compare(T,T);

  

如果有一个普通类型的数组,则无论传递数组本身,还是传递指针,都将调用模板版本。调用非模板版本的唯一途径是实参是constchar constchar * 指针数组的时候

    //调用compare(T,T)版本
    char ch_arr1[] = "world",ch_arr2[] = "hi";
    compare(ch_arr1,ch_arr2);

    char *p1 = "world",*p2 = "hi";
    compare(p1,p2);

    //调用compare(const char *,const char *)版本
    const char const_arr1[] = "world",const_arr2[] = "hi";
    compare(const_arr1,const_arr2);

    const char *cp1 = const_arr1,*cp2 = const_arr2;
    compare(cp1,cp2);

这些情况下,普通函数和函数模板完全匹配。当匹配同样好时,非模板版本优先。

【最佳实践】

设计既包含函数模板又包含非模板函数的重载函数集合是困难的,因为可能会使函数的用户感到奇怪,

因此:定义函数模板特化几乎总是比使用非模板版本更好!


//P573 习题16.61/62
template 
  
   
int compare(U,U,V)
{
    cout << "U,U,V" << endl;
    return 0;
}

int compare(const char *,const char *)
{
    cout << "cosnt char *" << endl;
    return 0;
}

template 
   
     int compare(T,T) { cout << "T,T" << endl; return 0; } int main() { char ch_arr1[] = "world",ch_arr2[] = "hi"; const char const_arr1[] = "world",const_arr2[] = "hi"; compare(ch_arr1,const_arr1); //const char * compare(ch_arr2,const_arr2); //const char * compare(0,0); //T,T } 
   
  

//习题16.63
template 
  
   
T calc(T,T)
{
    cout << "T,T" << endl;
}

double calc(double,double)
{
    cout << "double,double" << endl;
}

template <>
char calc
   
    (char,char) { cout << "char,char" << endl; } int main() { int ival; double dval; float fd; calc(0,ival); //T,T calc(0.25,dval); //double,double calc(0,fd); //double,double calc(0,'J'); //double,double }