C++普通函数与模板函数以及特化函数重载的优先级问题

在面对C++模板的时候,需要十分注意,因为模板的复杂性有很多情况,所以最好学习模板的方法我个人认为就是用到就去学,用不到就尽量别去看各种奇门怪技,因为你就算看了,好不容易搞懂模板的实现内部了,包括元编程啊什么的,但真正用到工作中的我相信很少,不久你也会忘掉,所以,对于模板,我们可以采取用到哪学到哪的观念去学习,这样可以节省时间并且让效率最大化。

今天主要讲在关于模板特化已经函数重载的问题,简单举下例子

1 void say(int value);

2 template <typename T>
  void say(T value);

3 template<>
  void say(int value);

如上三个函数,C++默认规定,如果存在非模板函数做到完全匹配(包括const和const &)就优先匹配非模板函数,然后匹配特化模板,然后匹配模板,所以上面的顺序是如果我调用say(1) 为 1 > 3 > 2 。注意,在匹配的过程中允许你的普通函数不需要做到所谓的“完全匹配”,可以有一些适当的变化比如:const或者const &,这点很关键.

在此我引用百度百科上对于重载的介绍来加深映像印象:

在重载及函数模板重载里,编译器选择函数,要经过以下三步,这个过程称为重载解析。

第一步:创建候选函数列表,其中包含有与被调函数名称相同的函数与模板函数。

第二步:使用候选函数列表创建可行函数列表。这些都是参数数目正确的函数

第三步:确定是否有最佳可行的函数。如果有,则使用。

确定最佳函数,只考虑其特征标,而不考虑返回类型(也无从考虑,但是要是硬想办法的话,也有,不过没有必要为了不必要的性能而浪费资源)。确定最佳函数,匹配特征标要依次经过以下判断:(1)完全匹配(常规函数优于模板;允许无关紧要的转换)(2)提升匹配(如char和short自动转换为int)(3)标准转换(int转换为char,long转换为double)(4)用户自定义的转换(如类声明中定义的转换函数)。

完全允许无关紧要的转换,这些转换包括引用,指针与实体之间,数组与指针之间,函数函数指针之间,const与非const等等。

最后,再附上一个比较容易搞混的demo

#include <iostream>
using namespace std;

template<class T>
void func(T &s)
{
    cout << "template version!" << endl;
    cout << sizeof(s) << ":\t" << s << endl;
}

void func(const char *s)
{
    cout << "special version!" << endl;
    cout << sizeof(s) << ":\t" << s << endl;
}

int main()
{
    char s[] = "asdasdasd";
    func(s);
    return 0;
}

这个demo,调用的时候会去调用templation的版本(songxiawuren提醒Windows下的cl默认是进special,我这里用的是gcc)。顺便摘录一个大牛的评论:

原因在于s对于模板函数来说是经历了一次identity conversion,其rank是Exact Match,普通函数时实参是数组类型,形参是指针类型,所以这里有array-to-pointer conversion和qualification conversions,两者的rank一样,最后的转换rank是Exact Match。然而,仅比较rank,都是一样,无区别。但是在相同情况下,identity conversion优于非indentity conversion,所以引用绑定的要优于后者.

所以我们可以试着去除const,因为没有了qualification conversions,这个时候就会优先去调用special的版本了。

参考链接: http://baike.baidu.com/view/126530.htm?fr=aladdin

http://bbs.csdn.net/topics/390577993

时间: 2024-07-29 23:41:09

C++普通函数与模板函数以及特化函数重载的优先级问题的相关文章

友元函数&lt;&lt;的模板化

1.构造函数的一种使用 int main(void){     //Test t1 = 10;// 在构造函数中寻找只有一个参数的          //手动的调用构造函数;     Test t2 = Test(1, 2);  //直接调用构造函数--->匿名对象;     //直接调用Test的有参构造函数,初始化t2对象;  只会调用一次构造函数(直接把匿名对象转成t2);      } 2.构造与赋值语句 Test g(){          //返回为对象,我就给你返回一个新的对象(匿

函数模板与模板函数及模板类与模板的特化

函数模板( Function templates) * 模板(Templates)使得我们可以生成通用的函数,这些函数能够接受任意数据类型的参数,可返回任意类型的值,而不需要对所有可能的数据类型进行函数重载.这在一定程度上实现了宏(macro)的作用.它们的原型定义可以是下面两种中的任何一个: template <class identifier> function_declaration; template <typename identifier> function_decla

模板(1)函数模板

1.引入 如何编写一个通用加法函数?第一个方法是使用函数重载, 针对每个所需相同行为的不同类型重新实现这个函数.C++的这种编程机制给编程者极大的方便,不需要为功能相似.参数不同的函数选用不同的函数名,也增强了程序的可读性.简单示例: 1 int Add(const int &_iLeft, const int &_iRight) 2 { 3 return (_iLeft + _iRight) ; 4 }f 5 loat Add(const float &_fLeft, const

虚函数和模板编程的一点共性和特征模板的一个例子

最近在看元编程中,对虚函数和模板编程有一点点感悟,写一篇博客简单总结一下. 虚函数和模板是C++里面很棒的特征,他们都提供了一种方法,让程序在编译中完成一些计算,去掉的这些计算在比较low的编程方式中,是需要在程序运行中执行的.在这里,我要强调的是:"在编译过程中完成一些计算". 我会举两个例子,一个是虚函数的,比较简单,另一个例子是关于特征模板的,在例子中,根据模板参数的类型自动选择模板的底层数据结构. 第一个例子是比较简单的虚函数的例子,有很多种水果的类型,我们有一个函数要展示他们

为什么不要特化函数模版?

/* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-priority:99; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm;

C++ 模板函数与模板类

一.模板函数 函数模板提供了一类函数的抽象,即代表了一类函数.当函数模板被实例化后,它会生成具体的模板函数.例如下面便是一个函数模板: 当实际调用它时,就会生成具体的模板函数:    模板函数在调用过程中会进行数据类型的自动匹配(在不产生歧义的情况下),但如果需要指定类型的话,可以显示声明,如: 这样,函数模板中的T就会被double所代替. 自动匹配有以下的规则: 1) 函数实参是否满足模板的实参(此时的判断没有数据类型的转换): 2) 若不满足1), 函数实参进行数据转换在进行匹配: 3)

通用模板实现可变参数函数

1 //模板 2 template <class T> 3 T getMax(T a, T b) 4 { 5 return a > b ? a :b; 6 } 7 8 9 template<typename T> 10 T MAX(T *p, const int n) 11 { 12 T max = p[0]; 13 for(int i = 1; i < n; i++) 14 { 15 if(max < p[i]) 16 { 17 max = p[i]; 18 }

【C++】C++问题——类模板分离编译、函数对象、智能指针

C++类模板的分离编译 过去很多类模板都是整个类连同实现都放在一个头文件里,像STL库就是遵循这样的策略来实现类模板的.现在的标准正试图矫正这种局面. 在实现中又许多函数模板.这意味着每个函数都必须包含模板声明,并且在使用作用域操作符的时候,类的名称必须通过模板变量来实例化. 比如一个operator=的代码: template <typename Object> const MemoryCell <Object> & MemoryCell<Object>::o

0722-----C++Primer听课笔记----------虚函数和模板

1.虚指针和虚函数表 1.1 不含有任何数据成员或者虚函数的class或者struct大小为1,含有虚函数的对象在基地址部分有一个vptr,指向虚函数表,因此大小为4个字节. 1.2 动态绑定的原理:假设派生类和基类存在覆盖的关系(基类中定义了虚函数),那么派生类在虚函数表中,会覆盖掉基类相应的虚函数.当程序执行的时候,根据基类指针找到vptr,根据vptr找到vtable,然后找到相应的版本去执行.所以执行的是覆盖的版本,而具体被哪个版本覆盖是由具体的对象类型所决定的,所以才实现了根据对象的具