C++总结之模版和泛型编程
http://blog.csdn.net/lightlater/archive/2010/08/08/5796719.aspx
三年来的实践,遇到的模版编程并不是很多,只是在最近的一个电信项目中,看到了高超的模版技术应用,十分感慨,这里对于模版的总结,完全参照Effective C++ 第三版。
隐式接口和编译期多态1.classes和template都支持接口(interfaces)和多态(polymorphism)。2.对于classes而言接口是显式的(explicit),以函数签名为中心。多态则是通过virtual函数发生在运行期。3.对于template参数而言,接口是隐式的(implicit),奠基于有效表达式。多态则是通过template实例化和函数重载解析(function overloading resolution)发生于编译期。
了解typename1.声明template参数时,前缀关键字class和typename可以互换。2.需要使用关键字typename标识嵌套从属类型名称;但是不能在base class lists(基类列)或member initialization list(成员初值列)内以它作为base class的修饰符。所谓从属名称,就是在template内出现的依赖于某个template参数的名称。关于typename,如下template<typename T>class Derived : public Base<T>::Nested // base class list中不允许出现"typename"{public: explicit Derived(int x) : Base<T>::Nested(x) // mem.ini.list中不允许"typename" { typename Base<T>::Nested temp; //嵌套从属类型名称,需要加上"typename"修饰 ... ... } };
3. 处理模版化基类内的名称当模版存在继承的时候,一定会导致继承自基类的名称无法通过编译,有三个方法可以解决该问题:我们假设有这样一个基类模版template<typename T>class MsgSender{public: ... ... void sendClear(const MsgInfo & info) // 继承类中将要调用的函数 { // send message. std::string msg = info.message(); T object; object.sendText(msg); } };当继承类需要使用函数sendClear时,编译时无法通过的,需要做如下选择:1.在base class函数调用动作之前加上”this->”:template<typename T>class LoggingMsgSender : public MsgSender<T>{public: ... ... void sendClearMsg(const MsgInfo & info) { // do something first this->sendClear(info); //使用“this->”的调用 // do something finally }};
2.使用using声明式可以做到:template<typename T>class LoggingMsgSender : public MsgSender<T>{public: using MsgSender<T>::sendClear; // 告诉编译器,假设sendClear位于base class内 ... ... void sendClearMsg(const MsgInfo & info) { // do something first sendClear(info); // 这里可以调用成功 // do something finally }};这里并不是base class内的名称被derived class名称遮掩,而是编译器根本就不进入base class作用域内查找,我们需要通过using告诉编译器,请它那么做。
3.明白指出被调用函数位于base class内:template<typename T>class LoggingMsgSender : public MsgSender<T>{public: ... ... void sendClearMsg(const MsgInfo & info) { // do something first MsgSender<T>::sendClear(info); // 直接调用 // do something finally }};这种做法还是欠妥,因为如果被调用的是个virtual函数,上述的明确资格修饰会关闭”virtual绑定行为”。
将与参数无关的代码抽离template1.template会生成多个classes和多个函数,所以任何template代码都不该与某个造成膨胀的template参数产生相依关系。2.因非类型参数模版而造成的代码膨胀,往往可以消除,做法就是以函数参数或者class成员变量替换template参数。3.因类型参数(type parameters)而造成的代码膨胀,往往可以降低,做法是让带有完全二进制表述的具现类型共享实现码。
运用成员函数模版接受所有兼容类型1.请使用member function template(成员函数模版)生成“可接受的所有兼容类型”的函数。2.如果我们声明member template用于“泛化 copy函数”或“泛化assignment操作”,那么还需要声明正常的copy函数和copy assignment操作符。
需要类型转换时请为模版定义非成员函数当我们编写一个class template,而它所提供的“与此template相关的”函数支持“所有参数的隐式转换”时,请将那些函数定义为“class template内部的friend函数”。典型的例子是:template<typename T> class Rational; // the Rational template.//helper templatetemplate<typename T>const Rational<T> doMultiply(const Rational<T> & lhs, const Rational<T> & rhs);
// template class Rational definitiontemplate<typename T>class Rational{public: ... ...
friend const Rational<T> operator *(const Rational<T> & lhs, const Rational<T> & rhs) { return doMultiply(lhs,rhs); }};
使用traits classes表现类型信息1.traits classes使得“类型相关信息”在编译期可用。他们以template和“templates特化”完成实现。如何设计并实现一个traits classes首先,确认若干你希望将来可取得的类型相关信息;其次,为该信息选择一个名称;第三,提供一个template和一组特化版本,内含我们希望支持的类型信息。2.整合重载技术后,traits classes有可能在编译期对类型进行测试。如何使用一个traits classes首先,建立一组重载函数或函数模版,彼此间的差异只在于各自的traits参数。令每个函数实现码与其接受的traits信息相符合;其次,建立一个控制函数或函数模版,它调用上述那些函数并传递traits class所提供的信息。
认识template元编程Template metaprogramming(TMP)是编写template-based C++程序并执行于编译期的过程。TMP两大效力:第一,它让某些事情更加容易。如果没有它,那些事情将是困难的,甚至是不可能的。第二,由于template metaprogramming执行于C++编译期,因此可将工作从运行期转移至编译期。这将导致一个结果是,某些错误原本通常在运行期才能侦测到,现在可在编译期找出来。另一个结果是,使用TMP的C++程序可能在每一个方面都更高效:较小的可执行文件,较短的运行期,较少的内存需求。TMP的一个典型例子是求阶乘:template<unsigned n>struct Factorial { enum{ value = n * Factorial<n-1>::value };};
template<>struct Factorial<0> { enum { value = 1 };};
使用方式如下:Factorial<10>::value; 这样就可以得到10的阶乘了
主线:《Effective C++》 Third Edition