12.2.6 优先级与执行顺序
总体来说,一般函数的执行优先与模板的特化函数相比,模板的特化函数优先于实例化函数,如示例代码12.6所示。
代码12.6 重载与优先级OverloadAndPRI
<————————————文件名:example1206.cpp——————————————-> 01 #include<iostream> 02 using namespace std; 03 template<class Ex> 04 Ex Greater(Ex x,Ex y); 05 template<>double Greater(double,double);//特化声明 06 int main() 07 { 08 int intX=1,intY=2; 09 double dblX=3. 0,dblY=2.9; 10 11 cout<<Greater(intX,intY)<<endl;//实例化 12 cout<<Greater(dblX,dblY)<<endl;//优先调用特化函数 13 cout<<Greater("world","hello")<<endl; 14 return 0; 15 } 16 template<class Ex> 17 Ex Greater(Ex x,Ex y) 18 { 19 return x>y?x:y; 20 } 21 template<>double Greater(double x,double y)//特化定义 22 { 23 return x+y; 24 }
输出结果如下所示。
2 5.9 Hello
【代码解析】前两个结果都是正确的,但字符串的比较出了问题,这是为什么呢?原来在代码第13行调用语句“Greater("world","hello")”时,编译器将类型参数Ex实例化为char*,由此根据模板Greater生成的模板函数如下所示。
charGreater(charx,char*y) { return x>y?x:y; }
比较的结果实际上是x和y两个字符串的地址大小,而不是字符串内容的比较,为char*构造特化函数是一个解决思路,此外还可以编写一个一般函数,同样会具有比实例化函数更高的优先级。
重载的普通函数如下所示。
charGreater(charx,char*y)//一般函数 { cout<<"一般函数被执行"<<endl; return(strcmp(x,y)>0)?x:y;//比较两个字符串的大小 }输出结果变为如下所示。 2 5.9 一般函数被执行 world
输出结果正确,普通函数优先被执行。
在某些情况下,对于一个函数调用,即使两个不同的函数模板都可以实例化,但是该函数调用仍然可能是二义的,这涉及编译器选择的问题。一般来说,更特化的模板函数优先,这是为什么显式实例化优先于隐式实例化的原因。
此外,更特化的含义体现在“编译器做决定时执行的转换最少”,C++遵循部分排序规则(Partial Ordering)来选择最优的模板函数。如果模板X匹配的参数集同样匹配于模板Y,但反之不成立,就说明模板X比模板Y更特化,如下例所示。
template<class T>void f(T){}#1 template<class T>void f(T*){}#2 template<class T>void f(const T*){}#3 template<class T>void g(T){}#4 template<class T>void g(T&){}#5 int main(){ const int*p; f(p); int q; //g(q); }
模板#2比模板#1更特化,模板#3比模板#2更特化,因此,f(p)调用的是“template<class T>void f(const T*){}”,对模板#4和模板#5来说,既不能说模板#4比模板#5更特化,也不能说模板#5比模板#4更特化,因此,g(q)的调用会出现二义性错误。
结合第9章中对重载函数选择规则的内容,可以看出重载解析用于寻找最匹配的函数,如果只有一个可选函数,则毫无疑义地调用它;如果有多个可选函数,首先选择普通函数,其次是模板函数;如果都是模板函数,则选择最特化的那个;如果有多个函数,并且编译器不能决定究竟要调用哪一个,编译器将会指出二义性错误,当然如果没有可选函数,也是错误。