支持泛型编程是c++非常强大的一个特性,我们可以通过定义一个模板函数和模板类,大大精简我们的代码,极大的增强程序的复用性和鲁棒性。之前本人一直对于泛型编程这部分了解的比较少,今天重新温习了一下,所以通过这篇博文来记录一下自己的一点心得。
首先我们还是以一个例子来开始,我定义了一个如下的函数模板用来实现,两个对象的比较功能:
-
template<typename T> int compare(const T &first, const T &second)
-
{
-
return first > second ? first : second;
-
}
上述模板非常的简单,就是用来返回两个对象中数值较大的对象的副本。在通常的情况下,这个模板工作上是不存在任何问题的,但是如果我们想要比较两个字符串的大小的时候,这个模板的工作,并不能让我们感到满意,因为我们在模板的形参中传递的是两个对象的引用,假如我们传递两个字符串指针的实参,其实模板中实现的仅仅是对于两个指针实参指向的地址的比较,而并不是指针所指向的内容的比较,我们如何去解决这样的一个问题呢,首先我们想到的是可以使用模板的特化,重新定义一个模板的特化版本来处理两个字符串的比较,于是就有了下面的模板特化:
-
template<> //全特化的表示方法
-
int compare(const char* const &first, const char* const &second)
-
{
-
return (strcmp(first, second) >= 0) ? first : second ;
-
}
上面的书写方式,是模板特化时候标准的书写形式,在本例中我们实现了一个基于字符串比较的功能,并且返回了两者中较大值。但是假如我们子书写特化版本的时候,忘记了template<>标志,书写成如下的形式:
-
int compare<const char*>(const char* const &first, const char* const &second)
-
{
-
return (strcmp(first, second) >= 0) ? first : second ;
-
}
结果会如何呢?这个时候编译器会告诉我们:
-
错误: 特例化成员‘::compare<const char*>’需要‘template<>’语法
假如我们忘记了compare后面尖括号的中类型的书写,或者更有甚者我们把两者都忘记了会出现什么情况呢:
-
int compare(const char* const &first, const char* const &second)
-
{
-
return strcmp(first, second);
-
}
-
-
template<>
-
int compare(const char* const &first, const char* const &second)
-
{
-
return strcmp(first, second);
-
}
这种情况下,编译器是否还会提示我们吗?经过编译发现,当上述两种情况同时存在的时候编译器编译通过。这个是什么原因呢?
我这个时候实际测试了一下,这两个函数,为了加以区别我们修改了一下第一个函数中实现我们改成下面:
-
int compare<const char*>(const char* const &first, const char* const &second)
-
{
-
return 0 - strcmp(first, second);
-
}
我们让第一个函数返回结果的相反数,然后我们来调用这个函数,如下:
-
int main()
-
{
-
-
int a = 10;
-
int b = 2;
-
const char *p1 = "dang";
-
const char *p2 = "dao";
-
cout << "resulst strcmp: " << compare(p1, p2) << endl;
-
cout << "result int: " << compare(a, b) << endl;
-
return 0;
-
}
大家可以想象一下,执行的结果:
-
resulst strcmp: 1
-
result int: 10
这说明了什么呢,说明在字符串比较的时候,调用了我们定义的修改之后的函数实现,而忽略了我们实现的漏掉了compare后面的尖括号中类型定义的特化版本,但是漏掉类型的定义并不是这里关键,即便我们把漏掉的尖括号加上,结果还是一样的。
原因到底什么呢?
是这样的,在c++之中,是允许用户重载一个函数的,所谓函数的重载,就是我们可以用同一个函数名,通过改变传入该函数的实参,来定义不同的函数实现。更为强大的一点是,c++也允许我们重载一个函数模板!!
c++中确定函数调用的步骤如下:
(1)为这个函数名建立一个候选函数的集合,包括:
a、与被调用函数重名的任意普通的函数。
b、任意函数模板的实例化,通过模板实参的推断匹配
(2)首先是查找哪些普通函数是可行的(确定这个普通函数是否可行,必须是传入实参和函数形参类型的精确匹配,经过类型转换匹配的不符合要求),然后才是查找模板中的实例来匹配。
所以我们会发现,在本例中我们陋写完成的函数特化,实际上是对于模板函数的重载,重载函数会优先于模板在函数调用时进行匹配,无论是原来的模板还是我们特化之后的模板,他们优先级都是要低于我们重载的函数,当然重载函数匹配需要形参和传入实参的完全匹配。
阅读(3206) | 评论(0) | 转发(0) |