一:废话

  今天在stackoverflow上看到一个关于c++模板specialization的问题:

http://stackoverflow.com/questions/18283851/template-specialization-in-case-of-multiple-base-templates-in-c/18283933?noredirect=1#comment26823443_18283933

他的English好像不是很标准(说不定是India三哥,哈哈),但比我强多了。废话不多说,问题简述如下:

  1. //#1
  2. template<class X> void foo(X a)
  3. {
  4. cout << "Template 1" << endl;
  5. }
  6.  
  7. //#2
  8. template<class X> void foo(X *a)
  9. {
  10. cout << "Template 2" << endl;
  11. }

现在如果定义一个特例化函数如下:

  1. template<> void foo<>(int *a)
  2. {
  3. cout << "Specialization 1" << endl;
  4. }

那么这哥们的问题如下:

1 这个函数是属于template #1呢还是属于template #2呢?

2 如果这个特例化定义在template #2之前和之后,结论会有差别吗?

  其实看过c++模板的内容,但没怎么用过复杂的模板,只是简单的写一些模板函数,方便适应不同的参数。 所以对模板特例化没有特别深入了解, 对偏序机制也就没有什么概念。正巧遇到这个哥们问了这样一个问题,我试着去回答,但是无能为力,正好有一位大神帮他回答了,于是我也顺便请教了这位大神,他说这个偏序化机制在模板中是一个比较复杂的概念,涉及内容比较多。了解了这个以后,我Google了一点资料,写下这篇小心得。

二: 偏序化(Partial Ordering)

应该是这么翻译吧!先看下什么叫partial ordering?引用参考资料1里面的介绍:

  1. A function template specialization might be ambiguous because template argument deduction might associate the specialization with
  2.  
  3. more than one of the overloaded definitions. The compiler will then choose the definition that is the most specialized. This process of selecting a function template definition is called partial ordering
 
在使用函数模板时,如果你定义了多个重载的特例化函数,可能导致模糊不清的调用,所以这时编译器会从中选择最特例的那个函数定义来调用。这个选择的机制就叫做偏序化。

三: 介绍

  在介绍之前,先看看什么叫最特例化?举个例子:

  1. //#1
  2. template<class T> void f(T);
  3.  
  4. //#2
  5. template<class T> void f(T*);
  6.  
  7. //#3
  8. template<class T> void f(const T*);

  上述三个模板中,特例化的程度从大到小依次为:

  1. #3 > #2 > #1

  如果现在有这样一个调用:

  1. int *p = NULL;
  2.  
  3. f(p);

  那么编译器肯定会选择#2模板,而不是#1模板,因为#2模板比#1模板更特例化。为什么不选#3模板?因为还有一个规则,优先选择类型显式匹配的模板,如果调用#3号模板,需要隐式转换。

  然后,接下来的问题是:编译器怎么知道#2模板比#1模板更特例化?下面就是我要说的partial ordering。编译器通过如下的方法来判断:

  1. 1 先选择两个函数模板,T1T2
  2.  
  3. 2 用假设的唯一类型X取代模板T1的参数
  4.  
  5. 3 用被X取代后的T1的参数列表,带入T2,看T2是否是一个有效的模板。忽略所有的隐式转换。
  6.  
  7. 4 反过来,先用X取代T2的参数,再把T2的参数列表带入T1,看看T1是否有效。
  8.  
  9. 5 如果一个模板的参数比如T1对于另外一个模板T2是有效的,但是反之不成立,那么就说这个模板T1不比T2更特例化。如果这两个模板的参数都可以相互代替,就说它们具有相同的特例性,这样会引起编译器混淆。

  举两个会引起混淆的一个例子,比如:

(1)

  1. template<class T> void g(T) { }
  2.  
  3. template<class T> void g(T&) { }
这两个模板的参数可以相互替代,所以编译器会报错。
 
(2)
  1. template<class T> void h(T) { }
  2.  
  3. template<class T> void h(T, ...) { } //error C2668: 'h' : ambiguous call to overloaded function

可变参数不会引起编译器执行partial ordering规则,所以这两种模板也会引起歧义。

四:使用

partial ordering的判断实例:

  1. 1 对于一个模板,特定类型的参数比一般类型的参数,更具有特例性
  2.  
  3. 2 带有T*的模板比T的模板具有特例性。因为一个假设的类型X*也可以被认为是T类型的, 相反一个有效的T类型参数,可能不是X*类型的。
  4.  
  5. 3 const TT更特例化,道理同上。
  6.  
  7. 4 const T*比const T更特例化,理由也是一样的。

举个例子:

  1. template <class T> void f(T) {
  2.  
  3. cout<<"f(T):Less specialized function called"<<endl;
  4.  
  5. }
  6.  
  7. template <class T> void f(T*) {
  8.  
  9. cout<<"f(T*):More specialized function called"<<endl;
  10.  
  11. }
  12.  
  13. template <class T> void f(const T*) {
  14.  
  15. cout<<"f(const T*):Even more specialized function for const T*"<<endl;
  16.  
  17. }
  18.  
  19. int _tmain(int argc, _TCHAR* argv[])
  20.  
  21. {
  22.  
  23. int i =;
  24.  
  25. const int j = ;
  26.  
  27. int *pi = &i;
  28.  
  29. const int *cpi = &j;
  30.  
  31. f(i); // Calls less specialized function.
  32.  
  33. f(pi); // Calls more specialized function.
  34.  
  35. f(cpi); // Calls even more specialized function.
  36.  
  37. // Without partial ordering, these calls would be ambiguous.
  38.  
  39. }

什么情况下,编译器会执行这样的一个Partial Ordering?文献1给出了几种情况:

  1. · Calling a function template specialization that requires overload resolution.
  2.  
  3. · Taking the address of a function template specialization.
  4.  
  5. · When a friend function declaration, an explicit instantiation, or explicit specialization refers to a function template specialization.
  6.  
  7. · Determining the appropriate deallocation function that is also a function template for a given placement operator new.

(1) 调用函数模板特例时,涉及到重载决议

(2) 获取函数模板特例的地址

(3) 当一个友元函数声明,或者显示实例化,或者引用函数模板的显示特例化

(4) 对一个new出来的内存进行销毁时(这个new函数也是模板函数),如何选择相应的释放函数也会引发partial ordering。

REFERENCE:

http://publib.boulder.ibm.com/infocenter/comphelp/v7v91/index.jsp?topic=%2Fcom.ibm.vacpp7a.doc%2Flanguage%2Fref%2Fclrc16part_ord_fn_tmpl.htm

http://msdn.microsoft.com/en-us/library/zaycz069.aspx

http://www.linuxtopia.org/online_books/programming_books/c++_practical_programming/c++_practical_programming_117.html

http://stackoverflow.com/questions/17005985/whats-the-partial-ordering-procedure-in-template-deduction

c++模板函数实例化的偏序机制的更多相关文章

  1. 使用 c++ 模板显示实例化解决模板函数声明与实现分离的问题

    问题背景 开始正文之前,做一些背景铺垫,方便读者了解我的工程需求.我的项目是一个客户端消息分发中心,在连接上消息后台后,后台会不定时的给我推送一些消息,我再将它们转发给本机的其它桌面产品去做显示.后台 ...

  2. C++:函数模板与模板函数

    6.1 模板的概念 C++允许用同一个函数定义函数,这些函数的参数个数和参数类型不同.例如求最大值的max函数, int max(int x,int y) {       return (x>y ...

  3. [C++]模板类和模板函数

    参考: C++ 中模板使用详解 C++模板详解 概念 为了避免因重载函数定义不全面而带来的调用错误,引入了模板机制 定义 模板是C++支持参数化多态的工具,使用模板可以使用户为类或者函数声明一种一般模 ...

  4. [转]C++函数模板与模板函数

      1.函数模板的声明和模板函数的生成   1.1函数模板的声明 函数模板可以用来创建一个通用的函数,以支持多种不同的形参,避免重载函数的函数体重复设计.它的最大特点是把函数使用的数据类型作为参数. ...

  5. 模板函数(template function)出现编译链接错误(link error)之解析

    总的结论:    将template function 或者 template class的完整定义直接放在.h文件中,然后加到要使用这些template function的.cpp文件中. 1. 现 ...

  6. C++ 模板函数与模板类

    一.模板函数 函数模板提供了一类函数的抽象,即代表了一类函数.当函数模板被实例化后,它会生成具体的模板函数.例如下面便是一个函数模板:

  7. 为什么模板函数的声明和实现都放在.h文件中

    当你不使用这个模板函数或模板类,编译器并不实例化它,当你使用时,编译器需要实例化它,因为编译器是一次只能处理一个编译单元,也就是一次处理一个cpp文件,所以实例化时需要看到该模板的完整定义.所以都放在 ...

  8. [c++][语言语法]函数模板和模板函数 及参数类型的运行时判断

    参考:http://blog.csdn.net/beyondhaven/article/details/4204345 参考:http://blog.csdn.net/joeblackzqq/arti ...

  9. C++ template学习一(函数模板和模板函数)

    函数模板和模板函数(1)函数模板函数模板可以用来创建一个通用的函数,以支持多种不同的形参,避免重载函数的函数体重复设计.它的最大特点是把函数使用的数据类型作为参数.函数模板的声明形式为:templat ...

随机推荐

  1. 我的c++学习(11)数组和指针

    使用数组显示斐波那契数列 #include "stdafx.h" using namespace std; #include<iostream> int _tmain( ...

  2. 洛谷 P1204 [USACO1.2]挤牛奶Milking Cows Label:模拟Ex 74分待查

    题目描述 三个农民每天清晨5点起床,然后去牛棚给3头牛挤奶.第一个农民在300秒(从5点开始计时)给他的牛挤奶,一直到1000秒.第二个农民在700秒开始,在 1200秒结束.第三个农民在1500秒开 ...

  3. Tornado 学习笔记13 TCPServer

         为了实现TCPServer的功能,定义一个类用于继承TCPServer并实现handle_stream方法.HttpServer就是一个很好的例子. 13.1 构造函数 def __init ...

  4. Glyphicon 字体图标

    Bootstrap中的Glyphicon 字体图标 在Bootstrap框架中也为大家提供了近200个不同的icon图片,而这些图标都是使用CSS3的@font-face属性配合字体来实现的icon效 ...

  5. Coreseek 安装指南

    Coreseek 中文官网:http://www.coreseek.cn/ Sphinx0.9.9 中文手册:http://www.coreseek.cn/docs/coreseek_3.2-sphi ...

  6. python: DOM 小实例

    一.全选 全部取消  反选 全选:选择指定的所有项目. 全部取消: 取消所有选定的项目. 反选: 选择未选定的,之前已选定的则取消. <!DOCTYPE html> <html la ...

  7. PHP 学习笔记---基本语法

    ------php语言与JavaScript的使用 方法是相似 <script type="text/javascript"> </script>--js与 ...

  8. curl 传递用户session

    $cmh = curl_multi_init(); $ch = curl_init(); curl_setopt($ch,CURLOPT_URL, $url); curl_setopt($ch, CU ...

  9. [IT新应用]家用NAS,自建“360云盘”

    360云盘也快要离开了.同事中有人开始尝试使用群晖NAS.西数的NAS来自建云了. [功能对比] [选择参数] [口碑评价]

  10. Yii源码阅读笔记(三十二)

    web/Application类的注释,继承base/Application类,针对web应用的一些处理: namespace yii\web; use Yii; use yii\base\Inval ...