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的迭代器有五种类型,卷标结构区分,之间有继承关系。

View Code
//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个矩阵连乘

可以生成客户定制的设计模式实现——编译期生成数以百计不同的智能指针

 

posted @ 2013-02-20 15:49  浑身胆满脸魄  阅读(201)  评论(0编辑  收藏  举报