[翻译] Effective C++, 3rd Edition, Item 43: 了解如何访问 templatized base classes(模板化基类)中的名字(下)

(点击此处,接上篇)

已知 MsgSender 针对 CompanyZ 被特化,再次考虑 derived class(派生类)LoggingMsgSender

template<typename Company>
class LoggingMsgSender: public MsgSender<Company> {
public:
  ...
  void sendClearMsg(const MsgInfo& info)
  {
    write "before sending" info to the log;

    sendClear(info);                          // if Company == CompanyZ,
                                              // this function doesn't exist!
    write "after sending" info to the log;
  }
  ...

};

就像注释中写的,当 base class(基类)是 MsgSender<CompanyZ> 时,这里的代码是无意义的,因为那个类没有提供 sendClear function(函数)。这就是为什么 C++ 拒绝这个调用:它认识到 base class templates(基类模板)可能被特化,而这样的 specializations(特化)不一定提供和 general template(通用模板)相同的 interface(接口)。结果,它通常会拒绝在 templatized base classes(模板化基类)中寻找 inherited names(继承来的名字)。在某种意义上,当我们从 Object-Oriented C++ 跨越到 Template C++(参见 Item 1)时,inheritance(继承)会停止工作。

为了重新启动它,我们必须以某种方式使 C++ 的 "don't look in templatized base classes"(不在模板基类中寻找)行为失效。有三种方法可以做到这一点。首先,你可以在被调用的 base class functions(基类函数)前面加上 "this->":

template<typename Company>
class LoggingMsgSender: public MsgSender<Company> {
public:

  ...

  void sendClearMsg(const MsgInfo& info)
  {
    write "before sending" info to the log;

    this->sendClear(info);                // okay, assumes that
                                          // sendClear will be inherited
    write "after sending" info to the log;
  }

  ...

};

第二,你可以使用一个 using declaration,如果你已经读过 Item 33,这应该是你很熟悉的一种解决方案。那个 Item 解释了 using declarations 如何将被隐藏的 base class names(基类名字)引入到一个 derived class(派生类)范围中。因此我们可以这样写 sendClearMsg

template<typename Company>
class LoggingMsgSender: public MsgSender<Company> {
public:
  using MsgSender<Company>::sendClear;   // tell compilers to assume
  ...                                    // that sendClear is in the
                                         // base class
  void sendClearMsg(const MsgInfo& info)
  {
    ...
    sendClear(info);                     // okay, assumes that
    ...                                  // sendClear will be inherited
  }

  ...
};

(虽然 using declaration 在这里和 Item 33 中都可以工作,但要解决的问题是不同的。这里的情形不是 base class names(基类名字)被 derived class names(派生类名字)隐藏,而是如果我们不告诉编译器去做,它们就不会搜索 base class 范围。)

最后一个让你的代码通过编译的办法是显式指定被调用的函数是在 base class(基类)中的:

template<typename Company>
class LoggingMsgSender: public MsgSender<Company> {
public:
  ...
  void sendClearMsg(const MsgInfo& info)
  {
    ...
    MsgSender<Company>::sendClear(info);      // okay, assumes that
    ...                                       // sendClear will be
  }                                           // inherited

  ...
};

通常这是一个解决这个问题的最不合人心的方法,因为如果被调用函数是 virtual(虚拟)的,显式限定会关闭 virtual binding(虚拟绑定)行为。

从名字可见性的观点来看,这里每一个方法都做了同样的事情:它向编译器保证任何后继的 base class template(基类模板)的 specializations(特化)都将支持 general template(通用模板)提供的 interface(接口)。所有的编译器在解析一个像 LoggingMsgSender 这样的 derived class template(派生类模板)时,这样一种保证都是必要的,但是如果保证被证实不成立,真相将在后继的编译过程中暴露。例如,如果后面的源代码中包含这些,

LoggingMsgSender<CompanyZ> zMsgSender;

MsgInfo msgData;

...                                          // put info in msgData

zMsgSender.sendClearMsg(msgData);            // error! won't compile

sendClearMsg 的调用将不能编译,因为在此刻,编译器知道 base class(基类)是 template specialization(模板特化)MsgSender<CompanyZ>,它们也知道那个 class(类)没有提供 sendClearMsg 试图调用的 sendClear function(函数)。

从根本上说,问题就是编译器是早些(当 derived class template definitions(派生类模板定义)被解析的时候)诊断对 base class members(基类成员)的非法引用,还是晚些时候(当那些 templates(模板)被特定的 template arguments(模板参数)实例化的时候)再进行。C++ 的方针是宁愿早诊断,而这就是为什么当那些 classes(类)被从 templates(模板)实例化的时候,它假装不知道 base classes(基类)的内容。

Things to Remember

  • 在 derived class templates(派生类模板)中,可以经由 "this->" 前缀,经由 using declarations,或经由一个 explicit base class qualification(显式基类限定)引用 base class templates(基类模板)中的名字。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值