C++之学习处理模板化基类内的名称(43)---《Effective C++》

条款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 Msg{...};
template <typename Company>
class MsgSender{
public:
    ...
    void sendClear(const MsgInfo& info){
        std::string msg;
        Company c;
        c.sendCleartext(msg);
    }
    //sendSecret函数类似于sendClear函数
    void sendSecret(const MsgInfo& info){
        ...
    }
};

这种做法行得通,但假设我们此时想要在每次送出消息时候志记某些信息。derived class可轻易加上这样的生产力,这似乎是个合情合理的解法:

template <typename Company>
class LoggingMsgSender:public MsgSender<Company>{
public:
    ...
    void sendClearMsg(const MsgInfo& info){
        sendClear(info);//调用base class函数,这段代码无法通过编译
    };
    ...
};

这里derived class的信息传送函数有一个不同的名称,与其base class内的名称不同,但上述代码无法通过编译,无法发现sendClear函数,为什么???
原因在于,当编译器遇到LoggingMsgSender定义式时,并不知道它继承什么样的class,当然它继承的是MsgSender<Company>,但其中的Company是个template参数,不到LoggingMsgSender被具现化,无法确切知道它是什么,而如果不知道Company是什么,当然也就无法知道class MsgSender看起来像什么—更明确的说是没办法知道他是否有个senderClear函数。

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

class CompanyZ{
public:
    ...
    void sendEncrypted(const std::string& msg);
    ...
};

一般行的MsgSender template对CompanyZ并不适合,因为template中多了一个sendClear函数,其中针对类型参数Company调用了sendCleartext函数,而这对CompanyZ对象并不合理。与纠正这个问题,我们针对CompanyZ产生一个MsgSender的特化版:

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

注意class定义式前面的“template <>”语法表示这既不是template也不是class,而是一个特化版的MsgSender template,在template实参是CompanyZ时被使用,这是所谓的模板全特化:tempalte MsgSender针对类型CompanyZ特化了,而且其特化是全面性的,也就是说一旦类型参数被定义为CompanyZ,在没有其他template参数可供变化。
现在,MsgSender针对CompanyZ进行了全特化,让我们重新考虑derived class LoggingMsgSender:

template <typename Company>
class LoggingMsgSender:public MsgSender<Company>{
public:
    ...
    void sendClearMsg(const MsgInfo& info){
        sendClear(info);//当Company==CompanyZ时候,这个函数并不存在
    }
    ...
};

正如注释所示,当base class被指定为MsgSender<<>CompanyZ>时候这段代码不合适,因为那个class并未提供sendClear函数!那就是为什么C++拒绝这个调用的原因:他知道base class templates可能会被特化,而那个版本可能不提供和一般性template相同的接口,因此他往往拒绝在templatized base classes内寻找继承而来的名称,就某种意义而言,当我们从Object Oriented C++迈入Template C++时候,继承就变得不像以前那样畅通无阻了。

为了解决这个问题,我们必须有办法令C++“不进入templatized base classes观察”的行为失效。有三个办法:

1、 在base class动作之前加上“this->”:

template <typename Company>
class LoggingMsgSender:public MsgSender<Company>{
public:
    ...
    void sendClearMsg(const MsgInfo& info){
        this->sendClear(info);
    }
    ...
};

2、使用using声明式:

template <typename Company>
class LoggingMsgSender:public MsgSender<Company>{
public:
    using MsgSender<Company>::sendClear;
    ...
    void sendClearMsg(const MsgInfo& info){
        ...
        sendClear(info);
        ...
    }
    ...
};

3、明确指出被调用的函数处于base class内:

template <typename Company>
class LoggingMsgSender:public MsgSender<Company>{
public:
    ...
    void sendClearMsg(const MsgInfo& info){
        ...
        MsgSender<Company>::senderClear(info);
        ...
    }
    ...
};

这种做法往往是最令人不满意的解法了,因为如果被调用的是virtual函数,上述的明确资格修饰会关闭“virtual绑定行为”。

PS:
在非模板类的函数中并不存在找不到函数的情况,详情见如下代码:

#include <iostream>
#include <string>
using namespace std;
class Base{
public:
    Base(int xx) :x(xx){

    }
    virtual void show(){
        cout << x << endl;
    }
    virtual void guessWhat()const = 0;
    virtual ~Base(){

    }
private:
    int x;
};
class Derived :public Base{
public:
    Derived(int x) :Base(x){

    }
    virtual void guessWhat()const{
        cout << "I'm so grateful for meeting you!!!" << endl;
    }
    void showHello(){
        show();
    }
};

int main(){
    Derived d(10);
    d.showHello();
    return 0;
}

运行结果:
这里写图片描述

PS:
针对全特化和偏特化,大家可以参考这篇博客哈,www.cnblogs.com/qlee/archive/2011/06/27/2091523.html,希望能够带给你更清晰的认识!!!

总结:
可在derived class templates中通过“this->”指涉base class templates内的成员名称,或者借由一个明白写出的“base class资格修饰符”完成。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值