Effective C++笔记_条款43 学习处理模板化基类内的名称

来源:互联网 发布:软件项目经理管理办法 编辑:程序博客网 时间:2024/05/21 11:34

Effective C++笔记_条款43 学习处理模板化基类内的名称

     开篇就来了一个示例代码,整个这个小节就围绕这个示例代码来描述模板化基类内的名称(函数)。主要是因为C++知道base class templates有可能被特化,而那个特化版本肯呢个不提供和一般性template相同的接口。因此它往往拒绝在templatized base classes(模板化基类)内寻找继承而来的名称。

复制代码
 1 class CompanyA { 2  public: 3      //... 4      void sendCleartext(const std::string& msg); 5      void sendEncrypted(const std::string& msg); 6      //... 7  }; 8  9  class CompanyB {10  public:11      //...12      void sendCleartext(const std::string& msg);13      void sendEncrypted(const std::string& msg);14      //...15  };16 17  //...  针对其他公司设计的类18  19  /*用来保存信息,以备将来产生信息*/20  class MsgInfo {21      //...22  };23 24 template<typename Company>25 class MsgSender {26 public:27     //... 构造函数、析构函数等28     void sendClear(const MsgInfo& info) 29     {30         std::string msg;31         // 根据info产生信息32         Company c;33         c.sendCleartext(msg);34     }35 36     void sendSecret(const MsgInfo& info)37     {38         std::string msg;39         // 根据info产生信息40         Company c;41         c.sendEncryptedtext(msg);42     }43 };44 45 /* 日志:记录每个信息发送相关记录 */46 template<typename Company>47 class LoggingMsgSender:public MsgSender<Company> {48     //... 构造函数、析构函数等49     void sendClearMsg(const MsgInfo& info) 50     {51 52         // 将“传送前”的信息写到log53         sendClear(info);             // 调用base class函数 (error:不同通过编译)54         // 将“传送后”的信息写到log55     }56 57 };58 59 /*调用base class函数 (error:不同通过编译)的原因:60   模板类型在编译前不知道Company是否有sendClear函数,比如下面这个 CompanyZ class61  */62 class CompanyZ {63     // 不提供sendClearText函数64     //...65      void sendEncrypted(const std::string& msg);66      //...67 };
复制代码

为了解决上述问题:可以使用为 CompanyZ,搞个绿色通道:
  1.class 定义式前面加 template<> 表明:它既不是template也不是标准的class,而是一个特化版的MsgSender template,在template实参是CompanyZ时被使用。
  2.模板全特化(total template specialization):template MsgSender针对类型CompanyZ特化了,而且其特化是全面性的,也就说一旦类型参数被指定为CompanyZ,再没有其他template参数可供变化。

复制代码
1 template<>            // 一个全特化的MsgSender,与一般template相同,差别只在于它删掉了sendClear2 class MsgSender<CompanyZ> {3     //...4     void sendSecret(const MsgInfo& info)5     {6         sendEncryptedtext(info);7     }8 };
复制代码
复制代码
 1 /* 再来看,日志:记录每个信息发送相关记录 */ 2 template<typename Company> 3 class LoggingMsgSender:public MsgSender<Company> { 4     //... 构造函数、析构函数等 5     void sendClearMsg(const MsgInfo& info)  6     { 7  8         // 将“传送前”的信息写到log 9         sendClear(info);             10         /*11             如果Company == CompanyZ, CompanyZ不存在sendClear函数,报错。12             原因:编译器知道base class templates有可能被特化,而那个特化版本肯呢个不提供13             和一般性template相同的接口。因此它往往拒绝在templatized base classes(模板化基类)内寻找继承而来的名称。14         */15         // 将“传送后”的信息写到log16     }17 18 };
复制代码

 

让C++“不进入templatized base classes观察”的行为失效的解决方法如下:

  1. 在 base class 函数调用动作之前加上 “this->”:

复制代码
 1 /* 日志:记录每个信息发送相关记录 */ 2 template<typename Company> 3 class LoggingMsgSender:public MsgSender<Company> { 4     //... 构造函数、析构函数等 5     void sendClearMsg(const MsgInfo& info)  6     { 7  8         // 将“传送前”的信息写到log 9         this->sendClear(info);             10         // 将“传送后”的信息写到log11     }12     //...13 };
复制代码

 

2.使用using声明式
  这里并不是base class名称被derived class名称遮掩,而是编译器不进入base class作用域内查找,于是通过using告诉它,请它那么做。

复制代码
 1 /* 日志:记录每个信息发送相关记录 */ 2 template<typename Company> 3 class LoggingMsgSender:public MsgSender<Company> { 4     using MsgSender<Company>::sendClear;            // 告诉编译器,清它假设sendClear位于base class内 5     //... 构造函数、析构函数等 6     void sendClearMsg(const MsgInfo& info)  7     { 8  9         // 将“传送前”的信息写到log10         sendClear(info);             11         // 将“传送后”的信息写到log12     }13     //...14 };
复制代码

 

3.明确指出被调用的函数位于base class内
  缺点:如果被调用的是virtual函数,明确自个修饰(explicit qualification) 会关闭"virtual绑定行为"

复制代码
 1 /* 日志:记录每个信息发送相关记录 */ 2 template<typename Company> 3 class LoggingMsgSender:public MsgSender<Company> { 4     //... 构造函数、析构函数等 5     void sendClearMsg(const MsgInfo& info)  6     { 7  8         // 将“传送前”的信息写到log 9         MsgSender<Company>::sendClear(info);             10         // 将“传送后”的信息写到log11     }12     //...13 };
复制代码

 

总结: 

  1. 三种方法都是从名称可视点(visibility point)的角度出发:

  对编译器承诺“base class template”的任何特化版本都将支持其一般(泛化)版本所提供的接口。[编译器的早期诊断时间:当解析derived class template的定义式时];如果这个承诺没有被实践,往后的编译器最终会给事实一个公道。[在编译器晚期诊断时间:当那些templates被特定的template具体化时]。如下面的示例:

1  LoggingMsgSender<CompanyZ> zMsgSender;2  MsgInfo msgData;3  //...4  zMsgSender.sendClearMsg(msgData);   // error

  2.可在derived class templates内通过"this->"指涉base class templates内的成员名称,或藉由一个明白写出的“base class资格修饰符”完成。


 

声明:全文文字都是来源《Effective C++》 3th。这里所写都是自己的读书的时候梳理做的笔记罢了。希望对他人有用,那是我的荣幸。

分类: C/C++
0 0