Effective c++ 7 41...48
41*了解隐式接口和编译期多态implicit interface, complie-time polymorphism
/*7 模板与泛型编程template & Generic programming 所写代码和其所处理的对象类型彼此独立 模板 元编程template metaprogramming */ /*41 隐式接口 implicit interface 编译期多态 complie-time polymorphism */ //widget.h #include <iostream> class Widget{ public: Widget(); virtual ~Widget(); virtual std::size_t size() const;//运行时多态 virtual void normalize();//运行时多态 void swap(Widget& other); //... }; //main void doProcessing(Widget& w){//w是显式接口explicit interface——Widget if (w.size()>10 && w !=someNastyWidget)//运行时多态runtime polymorphism { Widget temp(w); temp.normalize();//运行时多态 temp.swap(w); } } //变身 template<typename T>//function templates 函数模板——调用——直接具现化instantiated——编译期多态 void doProcessing(T& w){//w是什么接口?:由w的操作定,必须支持以下操作——隐式接口 implicit interface if (w.size()>10 && w !=someNastyWidget) { Widget temp(w); temp.normalize(); temp.swap(w); } }
42*typename的双重意义
/*42 typename的双重意义 */ //声明template参数时,class和typename等价 template<class T> class Widget; template<typename T>class Widget; //typename做嵌套从属名称前缀词 template<typename C> void print2nd(const C& container){ if (container.size() >=2 ) { typename C::const_iterator iter(container.begin()); //C::const_iterator是nested dependent names 嵌套从属名称 //编译器假设 C::const_iterator不是类型 //typename前缀词明确C::const_iterator是类型 ++iter; int value =*iter; std::cout<<value; } } //typename做嵌套从属名称前缀词的例外1,2 template<typename T> class Derived:public Base<T>::Nested{//1 public: explicit Derived(int x) :Base<T>::Nested(x)//2 { typename Base<T>::Nested temp; } }; //嵌套从属名称太长的时候,typedef是可以的 template<typename IterT> void workWithIterator(IterT iter){ //typename std::iterator_traits<IterT>::value_type temp(*iter); typedef typename std::iterator_traits<IterT>::value_type value_type; value_type temp(*iter); }
43:*模板基类中的名称
/*43 模板化基类里面的名称 */ class CompanyA{ public: void sendCleartext(const std::string& msg); void sendEncrypted(const std::string& msg); }; class CompanyB{ public: void sendCleartext(const std::string& msg); void sendEncrypted(const std::string& msg); }; class CompanyZ{ public: void sendEncrypted(const std::string& msg); }; class MsgInfo{/*...*/}; //模板基 template<typename Company> class MsgSender{ public: void sendClear(const MsgInfo& info){ std::string msg; /*构造msg*/ Company c; c.sendCleartext(msg); } void sendSecret(const MsgInfo& info){ std::string msg; /*构造msg*/ Company c; c.sendEncrypted(msg); } }; //全特化 template<> class MsgSender<CompanyZ>{ public: void sendSecret(const MsgInfo& info){ std::string msg; /*构造msg*/ CompanyZ cz; cz.sendEncrypted(msg); } }; //派生自模板基 template<typename Company> class LoggingMsgSender:public MsgSender<Company>{ public: //声称支持基类的所有接口(方法1或2或3)
using MsgSender<Company>::sendClear;//方法2 void sendClearMsg(const MsgInfo& info) { /*写到log*/ //sendClear(info);编译器拒绝查找【模板基类】 this->sendClear(info);//方法1 MsgSender<Company>::sendClear(info);//方法3 关闭virtual /*写log*/ } }; LoggingMsgSender<CompanyZ> zMsgSender; MsgInfo msgData; zMsgSender.sendClearMsg(msgData); //没有还是没有,编译器可以识别特化版本的基类 //其他的有这个函数的,可以用基类的这个函数
44*将与参数无关的代码抽离template
使用template可能会导致代码膨胀(code bloat):其二进制码带着重复(或几乎重复)的代码、数据,或两者。目标码(object code)臃肿。二进制浮夸。
/*44 与参数无关的代码 抽离template */ #include <iostream> /*version1 代码膨胀的典型例子*/ template<typename T, std::size_t matrixSize> //两个参数 class SquareMatrix{ public: void invert(){/*求逆矩阵*/ } }; int main() { //class template的成员函数只有在被使用时才被具化 //类型都是double 却具化出两个invert SquareMatrix<double,5>sm1; sm1.invert();//具化出一个invert SquareMatrix<double,10>sm2; sm2.invert();//具化出另一个invert return 0; }
修改,base版中只对“矩阵元素对象的类型”参数化,不对矩阵的尺寸参数化。仍保证两个参数的模板类的使用(用派生类实现)。
/*version2 以函数参数替换template参数 以class成员变量替换template参数 T相同时,只具化出一个invert*/ template<typename T> class SquareMatrixBase{ protected: SquareMatrixBase(std::size_t n,T* pMem): size(n),pData(pMem){} void setDataPtr(T* ptr){ pData = ptr; } //重新赋值给pData //这个函数与模板无关,类型都是double时,只具化出一个invert void invert(std::size_t matrixSize){/*以给定size求逆矩阵*/} private: std::size_t size;//矩阵的size T* pData;//矩阵的data(指针) };//问:直接用SquareMatrixBase 对象 调用public:invert呢?无法实现模板有2个参数。“可能会传递一个SquareMatrix<double,5>到一个期望获得SquareMatrix<double,10>的函数中去” //派生类决定矩阵内容1 template<typename T, std::size_t n> class SquareMatrix:private SquareMatrixBase<T>{ public: SquareMatrix(): SquareMatrixBase<T>(n,data){}//不需要动态分配 using SquareMatrixBase<T>::invert;//可以看到base版的invert void invert(){ this->invert(n); }//调用base版的invert(带size参数的那个,如果去掉了参数,是不是就分不出来了) private: T data[n*n];//派生类决定矩阵内容1,数据存储在SquareMatrix对象内部,对象自身可能非常大 }; //派生类决定矩阵内容2 template<typename T, std::size_t n> class SquareMatrix:private SquareMatrixBase<T>{ public: SquareMatrix(): SquareMatrixBase<T>(n,0),pData(new T[n*n]) { this->setDataPtr( pData.get() ); }//将刚new出的pData副本赋给base的pData using SquareMatrixBase<T>::invert; void invert(){ this->invert(n); } private: boost::scoped_array<T> pData;//派生类决定矩阵内容2,数据存储在heap(boost::scoped_array是为数组准备的智能指针) };
再进行调用时
int main() { //类型都是double SquareMatrix<double,5>sm1; sm1.invert(); SquareMatrix<double,10>sm2; sm2.invert(); //具化出一个基类SquareMatrixBase<double>,也就具化出一个invert() return 0; }
45*运用成员函数模板
//real指针,支持隐式转换是什么意思: class top{}; class middle:public top{}; class bottom:public middle{}; top* pt1 = new middle; top* pt2 = new bottom; const top* pct2 = pt1;
//目的 实现智能指针支持指针隐式转换 //用户自定义的智能指针指针不支持上述转换 //用户自定义的智能指针什么样子:一个包含(管理)real指针的完成指针功能的类,智能指针实际上是对象,这个对象还不支持real指针支持的隐式转换功能。 template<typename T> class SmartPtr{ public: explicit SmartPtr(T* realPtr);//以real指针完成初始化 可以是各种类型 }; //隐式转换是什么样子: class Top{}; class Middle:public Top{}; class Bottom:public Middle{}; SmartPtr<Top> pt1 = SmartPtr<middle>(new middle);//将SmartPtr<middle>转化为SmartPtr<Top> SmartPtr<Top> pt2 = SmartPtr<bottom>(new bottom);//将SmartPtr<bottom>转化为SmartPtr<Top> SmartPtr<const Top> pct2 = pt2;//将SmartPtr<Top>转化为SmartPtr<const Top>
现在,SmartPtr<T> 还不支持这种转化。
然后呢? 增加一个 泛化copy构造(成员函数模板)
template<typename T> class SmartPtr{ public: explicit SmartPtr(T* realPtr);//构造函数,以real指针完成初始化,可以是各种类型 template<typename U> SmartPtr(const SmartPtr<U>& other);//【】泛化copy构造函数(成员函数模板) }; //隐式转换是什么样子: class Top{}; class Middle:public Top{}; class Bottom:public Middle{}; SmartPtr<Top> pt1 ( SmartPtr<Middle>(new Middle) );//将SmartPtr<middle>转化为SmartPtr<Top> SmartPtr<Top> pt2 ( SmartPtr<Bottom>(new Bottom) );//将SmartPtr<bottom>转化为SmartPtr<Top> SmartPtr<const Top> pct2 ( pt2 );//将SmartPtr<Top>转化为SmartPtr<const Top>
现在,还支持翻过来的转换:还支持没啥关系的类型之间的隐式转换。
SmartPtr<Middle> pt1 ( SmartPtr<Top>(new Top) );
这并不合理。需要加上限制:只能派生类对象给基类指针。
//真实指针 class top{}; class middle:public top{}; class bottom:public middle{}; top* pt1 = new middle; top* pt2 = new bottom; const top* pct2 = pt1; template<typename T> class SmartPtr{ public: explicit SmartPtr(T* realPtr);//构造函数,以real指针完成初始化,可以是各种类型 template<typename U> SmartPtr(const SmartPtr<U>& other) :heldPtr( other.get() ) { };//泛化copy构造函数-模板函数 【限制】 T* get() const { return heldPtr; } private: T* heldPtr;//所持有的real指针 参数里的类型含有的real指针必须能赋值给正在构造的类型含有的real指针 }; //隐式转换是什么样子: class Top{}; class Middle:public Top{}; class Bottom:public Middle{}; SmartPtr<Top> pt1 ( SmartPtr<Middle>(new Middle) );//将SmartPtr<middle>转化为SmartPtr<Top> SmartPtr<Top> pt2 ( SmartPtr<Bottom>(new Bottom) );//将SmartPtr<bottom>转化为SmartPtr<Top> SmartPtr<const Top> pct2 ( pt2 );//将SmartPtr<Top>转化为SmartPtr<const Top>
46*Define non-member functions inside templates when type conversions are desired.
原文:当我们编写一个class template,与此template相关的函数支持“所有参数之隐式类型转换”,请将这个函数定义为“class template内部的friend函数”
全参数支持(隐转),意味着这个函数是【非成员函数】。∵成员函数自带的this指针参数不能隐式类型转换(条款24)。
template函数,意味着这个函数不能是纯非成员模板函数(∵编译器不确定模板函数参数类型时,不会隐转),需要声明在class template内部。∴这个函数被声明为了(class template内部的)friend函数。
//template1.h
template<typename T> class Rational;//声明Rational template template<typename T> const Rational<T> doMutiltify( const Rational<T>& lhs, const Rational<T>& rhs);//声明helper template template<typename T>//定义Rational template class Rational{ public: Rational(const T& numerator = 0, const T& denominator = 1):_numerator(numerator),_denominator(denominator){}//构造函数 const T numerator() const{return _numerator;}//获得分子 const T denominator() const{return _denominator;}//获得分母 //【令friend函数调用辅助函数,实现模板非成员函数,参数类型转换】 friend//把这个函数声明在类内部 const Rational operator*( const Rational& lhs,//类中的T,生成类对象时已经确认T的类型,2可以隐式转换 const Rational& rhs) { return doMutiltify( lhs, rhs ); }//减少inline函数冲击,只调用定义于class外部的辅助函数 private: T _numerator; T _denominator; }; template<typename T> const Rational<T> doMutiltify( const Rational<T>& lhs, //若有必要,在头文件内定义helper template const Rational<T>& rhs) { return Rational<T>( lhs.numerator()*rhs.numerator(), lhs.denominator()*rhs.denominator() ); }
//main.cpp
#include "template1.h" int main() { //如何使用 Rational<int> oneHalf(1,2); Rational<int> result = oneHalf*2; //2 如果operator*是成员模板函数,编译器识别不到operator*( const Rational<T>& lhs, //const Rational<T>& rhs)函数中去 return 0; }
47*使用traits classes表现类型信息 Use traits classes for information about types.
▶traits classes为什么存在?为了获得类型信息。
以stl的迭代器为例。stl的迭代器有五种类型,卷标结构区分,之间有继承关系。
//xutility.h //stl迭代器有着不同的能力,卷标结构区分,之间有继承关系 // ITERATOR TAGS struct input_iterator_tag { // identifying tag for input iterators }; struct output_iterator_tag { // identifying tag for output iterators }; struct forward_iterator_tag : public input_iterator_tag { // identifying tag for forward iterators }; struct bidirectional_iterator_tag : public forward_iterator_tag { // identifying tag for bidirectional iterators }; struct random_access_iterator_tag : public bidirectional_iterator_tag { // identifying tag for random-access iterators };
解决->需要获得迭代器类型的时候,该如何获得?的问题
//功能:迭代器iter偏移d template<typename IterT, typename DistT> void advance(IterT& iter, DistT d) { if (iter is a random access iterator)//需要判断iter的类型 { iter += d; } else{ if (d >= 0) { while (d--) ++iter; } else { while (d++) --iter; } } }
▶traits classes什么样子?
类型的traits信息必须位于类型自身之外。∵支持内置类型
标准技术:把traits信息放进一个template及其一个或多个特化版本中。
Std中就有若干个这样的template,与iterator有关的,是下面这个:
// TEMPLATE CLASS iterator_traits template<class _Iter> struct iterator_traits { // get traits from iterator _Iter typedef typename _Iter::iterator_category iterator_category;//【iterator_category】确认分类 条款42 typedef typename _Iter::value_type value_type; typedef typename _Iter::difference_type difference_type; typedef difference_type distance_type; // retained typedef typename _Iter::pointer pointer; typedef typename _Iter::reference reference; }; //+要求 用户自定义迭代器类型 嵌套一个typedef 名为iterator_category //deque 举例 list雷同 template<略> class deque{ public: class iterator{//迭代器的类 public: typedef random_access_iterator_tag iterator_category;//要求 }; }; //针对 内置指针的特化版本 template<class _Ty> struct iterator_traits<_Ty *> { // get traits from pointer typedef random_access_iterator_tag iterator_category;//【内置指针 直接就是random类的】 typedef _Ty value_type; typedef ptrdiff_t difference_type; typedef ptrdiff_t distance_type; // retained typedef _Ty *pointer; typedef _Ty& reference; };
▶traits classes如何用?
//重载doAdvance。差异只在于traits参数不一样(本例子中的第三个参数)。
//Advance根据类型调用对应doAdvance。效果:在编译期完成类型判定。不需要运行期的if...esle方式。另外,如果调用时是派生类类型实参,还可以调用基类形参的doAdvance。(∵基类能用的public派生类也能用) template<typename IterT, typename DistT> void doAdvance(IterT& iter, DistT d,std::random_access_iterator_tag) { iter += d; } template<typename IterT, typename DistT> void doAdvance(IterT& iter, DistT d,std::bidirectional_iterator_tag) { if (d >= 0) { while (d--) ++iter; } else { while (d++) --iter; } } template<typename IterT, typename DistT> void doAdvance(IterT& iter, DistT d,std::input_iterator_tag) { if ( d<0 ) { throw std::out_of_range("Negative distance"); } else { while (d--) ++iter; } } //advance调用doAdvace的合适版本 template<typename IterT, typename DistT> void advance(IterT& iter, DistT d) { doAdvance(iter,d,typename std::iterator_traits<IterT>::iterator_category) }
48*TMP (是图灵完全机器)
▶TMP有什么特征?
执行于编译期
完成不可能事件——什么?比如47中的例子,如果用常规ifesle判断,无法通过编译,虽然逻辑上不会进入,但语法上并不支持list::iter的+=操作。
工作从运行期提前到编译期,错误提前侦测——比如47中的类型测试。较小的可执行文件,较短的运行期,较少的内存需求。较长的编译时间。
Boost's MPL--针对TMP而设计的程序库。
▶例子2:比冬天吃冰淇淋还酷——在编译期计算阶乘,通过“递归模板具现化”实现循环:
template<unsigned n> struct Factorial{ enum{ value = n*Factorial<n-1>::value}; }; template<>//良好的递归:一个特殊情况造成递归的结束。 struct Factorial<0>{ enum{ value = 1 }; }; //***** int main() { cout<<Factorial<4>::value;//24 return 0; }
▶TMP能实现什么厉害的效果?
确保量度单位正确——复杂计算过程中的单位
优化矩阵运算——5个矩阵连乘
可以生成客户定制的设计模式实现——编译期生成数以百计不同的智能指针