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

模板与泛型编程

--重载与函数模板

引言:

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

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

一、函数匹配与函数模板

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

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

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

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

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

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

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

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

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

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

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

二、函数模板匹配的例子

template <typename T>
int compare(const T &,const T &);

template <class U,class V>
int compare(U,U,V);

int compare(const char *,const char *);

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

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

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

    compare(1,0);

    vector<int> 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_arr1和 ch_arr2转换为指针。因为两个函数一样匹配,所以普通函数优先于模板版本。

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

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

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

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

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

template <typename T>
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 <class U,class V>
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 <typename T>
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 <class T>
T calc(T,T)
{
    cout << "T,T" << endl;
}

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

template <>
char calc<char>(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
}
时间: 2024-10-13 04:44:32

C++ Primer 学习笔记_86_模板与泛型编程 -满载与函数模板的相关文章

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

模板与泛型编程 --重载与函数模板 引言: 函数模板可以重载:可以定义有相同名字但参数数目或类型不同的多个函数模板,也可以定义与函数模板有相同名字的普通非模板函数. 但是,声明一组重载函数模板不保证可以成功调用它们,重载的函数模板可能会导致二义性. 一.函数匹配与函数模板 如果重载函数中既有普通函数又有函数模板,确定函数调用的步骤如下: 1.为这个函数名建立候选函数集合,包括: a.与被调用函数名字相同的任意普通函数. b.任意函数模板实例化,在其中,模板实参推断发现了与调用中所用函数实参相匹配

C++ Primer 学习笔记_53_STL剖析(八):函数适配器:bind2nd 、mem_fun_ref 、函数适配器应用举例

回顾 五.STL中内置的函数对象 一.适配器 1.三种类型的适配器: (1)容器适配器:用来扩展7种基本容器,利用基本容器扩展形成了栈.队列和优先级队列 (2)迭代器适配器:(反向迭代器.插入迭代器.IO流迭代器) (3)函数适配器:函数适配器能够将仿函数和另一个仿函数(或某个值.或某个一般函数)结合起来. [1]针对成员函数的函数适配器 [2]针对一般函数的函数适配器 二.函数适配器 1.示例 #include <iostream> #include <algorithm> #i

模板与泛型编程1(函数模板)

定义.实例化函数模板: 对于函数体完全相同,唯一差异就是参数类型的情况,我们可以定义一个通用的函数模板,而非为每个类型都定义一个新函数: 1 #include <iostream> 2 #include <vector> 3 using namespace std; 4 5 template <typename T>//模板参数列表 6 int compare(const T &v1, const T &v2) { 7 if(v1 < v2) re

C++ Primer 学习笔记_77_模板与泛型编程 --实例化

模板与泛型编程 --实例化 引言: 模板是一个蓝图,它本身不是类或函数.编译器使用模板产生指定的类或函数的特定版本号.产生模板的特定类型实例的过程称为实例化. 模板在使用时将进行实例化,类模板在引用实际模板类型时实例化,函数模板在调用它或用它对函数指针进行初始化或赋值时实例化. 1.类的实例化 当编写Queue<int>qi时,编译器自己主动创建名为Queue<int>的类.实际上,编译器通过又一次编写Queue模板,用类型int取代模板形參的每次出现而创建Queue<int

C++ Primer 学习笔记_81_模板与泛型编程 --类模板成员[续1]

模板与泛型编程 --类模板成员[续1] 二.非类型形参的模板实参 template <int hi,int wid> class Screen { public: Screen():screen(hi * wid,'#'), cursor(hi * wid),height(hi),width(wid) {} //.. private: std::string screen; std::string::size_type cursor; std::string::size_type height

C++ Primer 学习笔记_82_模板与泛型编程 --类模板成员[续2]

模板与泛型编程 --类模板成员[续2] 六.完整的Queue类 Queue的完整定义: template <typename Type> class Queue; template <typename Type> ostream &operator<<(ostream &,const Queue<Type> &); template <typename Type> class QueueItem { friend clas

C++ Primer 学习笔记_75_模板与泛型编程 --模板定义

模板与泛型编程 --模板定义 引言: 所谓泛型程序就是以独立于不论什么特定类型的方式编写代码.使用泛型程序时,我们须要提供详细程序实例所操作的类型或值. 模板是泛型编程的基础.使用模板时能够无须了解模板的定义. 泛型编程与面向对象编程一样,都依赖于某种形式的多态性.面向对象编程中的多态性在执行时应用于存在继承关系的类.我们能够编写使用这些类的代码,忽略基类与派生类之间类型上的差异.仅仅要使用基类的引用或指针,基类类型或派生类类型的对象就能够使用同样的代码. 在泛型编程中,我们所编写的类和函数能够

C++ Primer 学习笔记_84_模板与泛型编程 --模板特化

模板与泛型编程 --模板特化 引言: 我们并不总是能够写出对全部可能被实例化的类型都最合适的模板.某些情况下,通用模板定义对于某个类型可能是全然错误的,通用模板定义或许不能编译或者做错误的事情;另外一些情况下,能够利用关于类型的一些特殊知识,编写比从模板实例化来的函数更有效率的函数. compare函数和 Queue类都是这一问题的好样例:与C风格字符串一起使用进,它们都不能正确工作. compare函数模板: template <typename Type> int compare(cons

C++ Primer 学习笔记_83_模板与泛型编程 --一个泛型句柄类

模板与泛型编程 --一个泛型句柄类 引言: [小心地雷] 这个例子体现了C++相当复杂的语言应用,理解它需要很好地理解继承和模板.在熟悉了这些特性之后再研究这个例子也许会帮助.另一方面,这个例子还能很好地测试你对这些特性的理解程度. 前面示例的Sales_item和Query两个类的使用计数的实现是相同的.这类问题非常适合于泛型编程:可以定义类模板管理指针和进行使用计数.原本不相关的Sales_item类型和 Query类型,可通过使用该模板进行公共的使用计数工作而得以简化.至于是公开还是隐藏下