c++泛型编程中函数模板重载和模板特化同时存在时的查找规则

作者:风箫夜吟 和c/c++相关  
支持泛型编程是c++非常强大的一个特性,我们可以通过定义一个模板函数和模板类,大大精简我们的代码,极大的增强程序的复用性和鲁棒性。之前本人一直对于泛型编程这部分了解的比较少,今天重新温习了一下,所以通过这篇博文来记录一下自己的一点心得。
首先我们还是以一个例子来开始,我定义了一个如下的函数模板用来实现,两个对象的比较功能:

点击(此处)折叠或打开

  1. template<typename T> int compare(const T &first, const T &second)
  2. {
  3.     return first > second ? first : second;
  4. }
上述模板非常的简单,就是用来返回两个对象中数值较大的对象的副本。在通常的情况下,这个模板工作上是不存在任何问题的,但是如果我们想要比较两个字符串的大小的时候,这个模板的工作,并不能让我们感到满意,因为我们在模板的形参中传递的是两个对象的引用,假如我们传递两个字符串指针的实参,其实模板中实现的仅仅是对于两个指针实参指向的地址的比较,而并不是指针所指向的内容的比较,我们如何去解决这样的一个问题呢,首先我们想到的是可以使用模板的特化,重新定义一个模板的特化版本来处理两个字符串的比较,于是就有了下面的模板特化:

点击(此处)折叠或打开

  1. template<> //全特化的表示方法
  2. int compare<const char*>(const char* const &first, const char* const &second)
  3. {
  4.     return (strcmp(first, second) >= 0) ? first : second ;
  5. }
上面的书写方式,是模板特化时候标准的书写形式,在本例中我们实现了一个基于字符串比较的功能,并且返回了两者中较大值。但是假如我们子书写特化版本的时候,忘记了template<>标志,书写成如下的形式:

点击(此处)折叠或打开

  1. int compare<const char*>(const char* const &first, const char* const &second)
  2. {
  3.     return (strcmp(first, second) >= 0) ? first : second ;
  4. }
结果会如何呢?这个时候编译器会告诉我们:

点击(此处)折叠或打开

  1. 错误: 特例化成员‘::compare<const char*>’需要‘template<>’语法
假如我们忘记了compare后面尖括号的中类型的书写,或者更有甚者我们把两者都忘记了会出现什么情况呢:

点击(此处)折叠或打开

  1. int compare(const char* const &first, const char* const &second)
  2. {
  3.     return strcmp(first, second);
  4. }

  5. template<>
  6. int compare(const char* const &first, const char* const &second)
  7. {
  8.     return strcmp(first, second);
  9. }
这种情况下,编译器是否还会提示我们吗?经过编译发现,当上述两种情况同时存在的时候编译器编译通过。这个是什么原因呢?
我这个时候实际测试了一下,这两个函数,为了加以区别我们修改了一下第一个函数中实现我们改成下面:

点击(此处)折叠或打开

  1. int compare<const char*>(const char* const &first, const char* const &second)
  2. {
  3.     return 0 - strcmp(first, second);
  4. }
我们让第一个函数返回结果的相反数,然后我们来调用这个函数,如下:

点击(此处)折叠或打开

  1. int main()
  2. {

  3.     int a = 10;
  4.     int b = 2;
  5.     const char *p1 = "dang";
  6.     const char *p2 = "dao";
  7.     cout << "resulst strcmp: " << compare(p1, p2) << endl;
  8.     cout << "result int: " << compare(a, b) << endl;
  9.     return 0;
  10. }
大家可以想象一下,执行的结果:

点击(此处)折叠或打开

  1. resulst strcmp: 1
  2. result int: 10
这说明了什么呢,说明在字符串比较的时候,调用了我们定义的修改之后的函数实现,而忽略了我们实现的漏掉了compare后面的尖括号中类型定义的特化版本,但是漏掉类型的定义并不是这里关键,即便我们把漏掉的尖括号加上,结果还是一样的。

原因到底什么呢?

是这样的,在c++之中,是允许用户重载一个函数的,所谓函数的重载,就是我们可以用同一个函数名,通过改变传入该函数的实参,来定义不同的函数实现。更为强大的一点是,c++也允许我们重载一个函数模板!!

c++中确定函数调用的步骤如下:
(1)为这个函数名建立一个候选函数的集合,包括:
a、与被调用函数重名的任意普通的函数。
b、任意函数模板的实例化,通过模板实参的推断匹配
(2)首先是查找哪些普通函数是可行的(确定这个普通函数是否可行,必须是传入实参和函数形参类型的精确匹配,经过类型转换匹配的不符合要求),然后才是查找模板中的实例来匹配。

所以我们会发现,在本例中我们陋写完成的函数特化,实际上是对于模板函数的重载,重载函数会优先于模板在函数调用时进行匹配,无论是原来的模板还是我们特化之后的模板,他们优先级都是要低于我们重载的函数,当然重载函数匹配需要形参和传入实参的完全匹配。












相关资料:

c++泛型编程中函数模板重载和模板特化同时存在时的查找规则来源网络,如有侵权请告知,即处理!

编程Tags: