C++ 学习处理模板化基类里的名称

20180330 C++ 学习处理模板化基类里的名称


假设我们需要撰写一个程序,它能够传送信息到若干不同的公司去,信息要不译成密码,要不就是未经加工的文字,若编译期间我们有足够信息来决定哪一个信息传至哪一个公司,就可以采用基于模板(template)的解法:


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 MsgInfo{...};   //这个类用来保存信息,以备将来产生信息




template<typename Company>
class MsgSender
{
public:
  ...       //构造函数、析构函数等
  void sendClear(const MsgInfo& info)
  {
    std::string msg;
    
    /*这里,根据info产生信息*/


    Company c;
    c.sendCleartext(msg);
  }
 
    void sendSecret(const MsgInfo& info)//类似sendClear(),唯一不同的是这里调用的
                                          //是c.sendEncrypted
    {...}
  
}






这样的写法行得通,我们再假设想要在每次送出信息时记录(log)某些信息。派生类可轻易实现这个功能,下面这个解法似乎合情合理:
template<typename Company>


class LoggingMagSender:public MsgSender<Company>
{
public:
  ...
  void sendClearMsg(const MsgInfo& info)
  {


    /*这段代码是将"传送前"的信息写到log里*/


    sendClear(info);   //调用基类函数,这段代码无法通过编译


    /*这段代码是将"传送后"的信息写到log里*/
    
  }
}












上述代码无法通过编译,至少对严守规律的编译器而言,这样的编译器会抱怨sendClear不存在,但是我明明看到sendClear在父类中啊! 编译器却看不到它,这是为什么呢?




这是因为当编译器遭遇类模板(class template) LoggingMagSender定义式时,并不知道它继承说明样的类,当然它继承的是MsgSender<Company>,但其中的Company是个模板(template)参数,不到后来(当LoggingMagSender被具现化)无法确切知道它是什么,而如果不知道Company是什么,就无法知道 class  MsgSender<Company>看起来像什么--更确切的说是 没办法制导它是否有个sendClear()函数。




为了让问题更具体化,假设我们有个CompanyZ坚持使用加密通讯:


class CompanyZ        //这个类不提供
                      //sendCleartext函数
{
public:
  ...
  void sendEncrypted(const std::string& msg);
  ...
};




一般性的MsgSender template对CompanyZ并不合适,因为那个模板(template)提供了一个sendClear函数(其中针对其类型参数Company调用了sendCleartext函数),而这对CompanyZ对象并不合理,欲矫正这个问题,完美可以针对CompanyZ产生一个MsgSender特化版:
tempalte<>       


/*
一个全特化的MsgSender,它和一般的模板(templated)相同,
差别只在于它删除了sendClear。
*/


class MsgSender<CompanyZ>
{
public:
  ...
  void sendSecret(const MsgInfo& info)
  {...}
};






注意:类(class)定义式最前头的”template<>“语法象征这既不是模板也不是标准类,而是个特化版的MsgSender template,当template的实参是CompanyZ时才被使用,这就是所谓的模板全特化(total template specialization): template MsgSender针对类型CompanyZ特化了,而且其特化是全面性的,,也就是说一旦类型参数被定义为CompanyZ,再没有其他template参数可供变化。


现在,MsgSender针对CompanyZ进行了全特化,让我们再次考虑派生类LoggingMagSender:








template<typename Company>


class LoggingMagSender:public MsgSender<Company>
{
public:
  ...
  void sendClearMsg(const MsgInfo& info)
  {


    /*这段代码是将"传送前"的信息写到log里*/


    sendClear(info);   //若 Company = CompanyZ,则这个函数不存在


    /*这段代码是将"传送后"的信息写到log里*/
    
  }
}






正如注释所言,当基类被指定为MsgSender<CompanyZ>时,这段代码不合法,因为那个类并未提供sendClear()函数!,这就是C++为何拒绝调用的原因:
它知道基类模板有可能被特化,而那个特化版本可能不提供和一般性模板相同的接口,因此它往往拒绝在模板化基类(templatized base  class.本例的MsgSender<Company>)里寻找继承而来的名称(本例的sendClear),就某种意义而言,当我们从面向对象C++跨进模板C++,继承就不像以前那么畅通无阻了。




我们重头再来看,完美必须让这一行为失效,即: 不进入模板化基类观察。在这里有三种方法:
方法一:在基类调用动作之前加上“this->”,eg:


template<typename Company>


class LoggingMagSender:public MsgSender<Company>
{
public:
  ...
  void sendClearMsg(const MsgInfo& info)
  {


    /*这段代码是将"传送前"的信息写到log里*/


    this->sendClear(info);   //成立,假设sendClear将会被继承


    /*这段代码是将"传送后"的信息写到log里*/
    
  }
}








方法一:使用using声明式。


template<typename Company>


class LoggingMagSender:public MsgSender<Company>
{
public:
  ...
  void sendClearMsg(const MsgInfo& info)
  {


    using MsgSender<Company>::sendClear;// 告诉编译器,请他假设sendClear位于基类中


    /*这段代码是将"传送前"的信息写到log里*/


    sendClear(info);   //成立,假设sendClear将会被继承


    /*这段代码是将"传送后"的信息写到log里*/
    
  }
}








方法一:明白指出被调用的函数位于基类里。


template<typename Company>


class LoggingMagSender:public MsgSender<Company>
{
public:
  ...
  void sendClearMsg(const MsgInfo& info)
  {


    /*这段代码是将"传送前"的信息写到log里*/


    MsgSender<Company>::sendClear(info);   //成立,假设sendClear将会被继承


    /*这段代码是将"传送后"的信息写到log里*/
    
  }
}


这个方法是最不令人满意的解法,因为若被调用的是虚函数,上述的明确资格修饰(explicit qualification)会关闭 "虚绑定行为"。












注意:可在派生类模板(derived class template)内通过"this->"指涉基类模板(base class template)内的成员名称,或借由一个明白写出的"基类(base class)资源修饰符"完成。




猜你喜欢

转载自blog.csdn.net/weixin_39089680/article/details/79930370