想做一个自己的元胞自动机模板库,结果就把自己搞晕了:
1. Template Template Parameters
想在模板类中封装一个容器,容器的类型可以由用户指定,容器中元素的类型也可以由用户指定。
于是想当然而
template<typename CellType, typename ContainerType>
class CellContainer
{
......
ContainerType<CellType> m_cellSet;
......
}
想当然而的东西当然不对,通不过编译,自己也知道,恐怕没有这么简单解决。找了google这个最好的老师,搜到C++ Common Knowledge: Essential Intermediate Programming 这本书,还有电子版。原来这个技术叫做Template Template Parameters,如获至宝。
捣鼓了半天,这么做了:
template<typename CellType, template <typename> class ContainerType>
class CellContainer
{
......
ContainerType<CellType> m_cellSet;
......
}
template <typename> class 表示后面的那个ContainerType也是一个模板类,而且是含有一个模板参数的模板类。
光是模板类的编译是通过了,不过对于模板来说,不到具现它的时候是说明不了问题的。
果然,CellContainer<int, vector>
通过不了编译。说是vector和模板不匹配。
再次想办法,这回总算捡起《C++ template》这本经典了,(我的pdf板很差,不大高兴看它)。
终于找到,原来,STL容器是有默认Allocator的,只是我们从来不用,一律default。但是在这里,必须要告诉编译器,有两个模板参数才行。
template<typename CellType,
template<typename Elem,typename Alloc = std::allocator<Elem>> class ContainerType>
class CellContainer
{
ContainerType<CellType> m_cellSet;
};
template<typename Elem,typename Alloc = std::allocator<Elem>> class
表示后面的ContainerType是一个含有两个模板参数的模板类,并且第二个模板参数默认为std::allocator<Elem>
这样才和STL的Container对上了。
唉,还是自己学艺不精啊!STL基本的没搞透彻。
2 Template Specialization
template给我们的力量在于:对不同的型别的一致性处理。
但是,在设计template function或者template class的时候,型别的行为并不一定就是我们所预期的那样。当有的型别符合我们设计的意愿,但是又不容易融合到我们的template中来的时候,我们可以特殊处理之,那就叫特化( Specialization)。
例如(Modern C++ Design)中的例子
有模板类:
template <class Window, class Controller>
class Widget
{
... generic implementation ...
};
针对特定的ModalDialog, MyController类,我可以做特殊的处理:
template <>
class Widget<ModalDialog, MyController>
{
... specialized implementation ...
};
这样,当模板参数为ModalDialog, MyController时,我们的Widget的行为是后面特化后的行为。
如果,我们紧紧希望对MyController做特化,而这样的特化适用于任意Window与MyController的结合,那么我们可以做偏特化:
Partial Template Specialization
template <class Window>
class Widget<Window, MyController>
{
... partially specialized implementation ...
};
偏特化还有重要的一个用途就是在处理类似指针行为的型别的时候:
看例子(C++ templates : The Complete Gui
template<typename T> class List { // (1) public: … void append(T const&); inline size_t length() const; … };
这是一个List的模板类,但有时我们希望一个List中能存放不同类型的元素。在过去,我们利用指针void *做到这一点,
因为void*指针可以指向任意类型的对象。
而我们也知道,我们处理List<int*>::append() 和List<void*>::append() 的方式其实是一样的。也就是针对指针
型的型别,我们可以采用统一的算法。于是,我们可以专门针对指针型的元素,做上述模板类的偏特化
template<typename T> class List<T*> { // (2) private: List<void*> impl; … public: … void append(T* p) { impl.append(p); } size_t length() const { return impl.length(); } … };不过这样又带来一个问题,因为偏特化的过程中,一个成员就是List<void*> impl; 为了能够实现List<void*> 本身,我们再针对List<void*>做一个特化template<> class List<void*> { // (3) … void append (void* p); inline size_t length() const; … };真是麻烦啊,感觉特化就是模板搞不定的事情的修修补补,毕竟静态多态是算法一致,类型不一致,但是算法又是对型别有要求的,所以搞起来还是比动态多态麻烦。但是template是麻烦模板设计者,幸福模板用户的。对于用户来说,能获得又灵活又高效的库是一件幸福的事情。效率,弹性,简单三大目标不可能兼得,C++取的是效率和弹性,简单只能被放弃了。