STL就象一个大的类型推导机器. 她把类型做为参数,用推导做为运算.通过匹配规则来确实最后的调用.
1.类型
有简单的如int ,有复杂的如 int (class_A::*)(Param_A*)
通过推导,能把类型分解,如
- template<class T1, class T2, class T3>
- void f3(T1 (T2::*)(T3*));
- template<class E, int N>void f2(E (&)[N]);
2.推导
可以正向匹配得到,如
- max<int>(1, 2);
也能反向匹配得到,如
- template<typename T> void f(T, T);
- void (*pf)(char, char) = &f;
- class S
- {
- public:
- template<typename T, int N> operator T[N]&();
- };
- void f(int (&)[20]);
- void g(S s) {f(s); }
模板函数的推导除了隐式的不家显式的,一些看起来有冲突的模板函数实际上是可以
共存的,通过设定显式类型参数,可以让它们得到正确的推导.
如
- template<typename T> int f(T) { return 1; }
- template<typename T> int f(T*) { return 2; }
- #include <iostream>
- #include "funcoverload.hpp"
- int main() {
- std::cout << f<int*>((int*)0) << std::endl;
- std::cout << f<int>((int*)0) << std::endl;
- }
This program has the following output:
1 2
3.信息收集与调用时机
函数被调用时,编译器能根据调用时的信息(包括函数名,参数等等函数要素)
确定被调用的函数.这样就好处是可以支持重载,不好的地方就是一写出来就是调用,
没办法做被回调样式的函数.
把函数做成伪函数对象(把函数指针做为参数,传递给适配器模板,生成一个对象,
对象提供一些operator()的类似函数调用的重载操作符),支持回调样式,调用的时机
待定,这是他的好处,缺点是函数不是完整的,支持重载比较麻烦(没有了重载函数
的编译器自动识别的好处),必须在适配器模板上写出完整的函数信息,好让编译去定
位函数(得不偿失).
4.模板的算法机制
把类型做为待机的参数,调动编译的类型匹配机制,能作出许多非模板下无法达到的功
能.(待补充)
5.差异与统一
统一是理想的,差异是现实的.如何抹掉不同的特性呢,可能有许多办法可以做到.我们
可以从std::iterator::iterator_category得到一些启示.借助了iterator_category和编译
器的重载定位功能,比较有趣的地方是把iterator_category作为纯类型(没有实参功能,
STL中有很多地方都有类似的只提供类型的结构体)参数,让编译实现重载函数的定位.
轻易的抹掉了不同容器迭代器之间的差异(由重载函数去完成差异调用),体现了很好
的信息屏蔽.
我发现上述的这个实现与类的虚拟函数达到多态效果有许多相通的地方,不过前者是
在编译时完成,后者则是在运行时确实.
模板把编译的功能发挥到了极致(至少是我所能看到的),也让人不得不去深研编译的
类型推导机制.编译器这个幕后英雄越来越引人关注.