• 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         // 将“传送前”的信息写到log
    53         sendClear(info);             // 调用base class函数 (error:不同通过编译)
    54         // 将“传送后”的信息写到log
    55     }
    56 
    57 };
    58 
    59 /*调用base class函数 (error:不同通过编译)的原因:
    60   模板类型在编译前不知道Company是否有sendClear函数,比如下面这个 CompanyZ class
    61  */
    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相同,差别只在于它删掉了sendClear
    2 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         // 将“传送后”的信息写到log
    16     }
    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         // 将“传送后”的信息写到log
    11     }
    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         // 将“传送前”的信息写到log
    10         sendClear(info);             
    11         // 将“传送后”的信息写到log
    12     }
    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         // 将“传送后”的信息写到log
    11     }
    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。这里所写都是自己的读书的时候梳理做的笔记罢了。希望对他人有用,那是我的荣幸。

  • 相关阅读:
    网恋现代人的童话
    男人爱女人
    在Web页面中管理服务
    wcf使用入门学习笔记
    table显示细线边框
    wcf整理资料
    启动sqlserver服务的时候报错
    命名规范
    .net中使用xsl文件作为导航菜单
    wcf如何选择绑定
  • 原文地址:https://www.cnblogs.com/cloudfeng/p/4419219.html
Copyright © 2020-2023  润新知