《Effective C++》:条款43:学习处理模板化基类内的名称

[toc] 模板化的类作为基类时,有哪些要注意的地方。以一个例子说明,假设现在编写一个发送信息到不同公司的程序,信息要么译成密码,要么就是原始文字,在编译期间来决定哪一家公司发送至哪一家公司,采用template手法:

class CompanyA{public:void sendCleartext(const std::string& msg);void sendEncryted(const std::string& msg);……};class CompanyB{public:void sendCleartext(const std::string& msg);void sendEncryted(const std::string& msg);……};……<typename Company>class MsgSender{public:……//构造析构等函数void sendClear(const MsgInfo& info){std::string msg;//根据info产生信息Company c;c.sendCleartext(msg);}void sendSecart(const MsgInfo& info){……}};

上面这个做法行得通,但是如果要在每次送出信息时记录日志,可以派生出derived class,加上记录的日志

template<typename Company>class LoggingMsgSender: public MsgSender<Company>{public:……//析构构造等void SendClearMsg(const MsgInfo& info){//发送前的信息写到logsendClear(info);//调用base class函数,这段代码无法通过编译//传送后信息写到log}};

在派生类中,sendClearMsg和base class中的sendClear不同,这是个好设计,避免遮掩继承而得的名称,也避免了重新定义一个继承而得non-virtual函数。但是编译不能通过,因为编译器看不到sendClear函数。

因为当编译器遇到class template LoggingMsgSender定义式时,不知道它继承什么样的class,因为MsgSender中的Company是个参数,在LoggingMsgSender被具体化之前,无法确切知道它是什么,自然而然就不知道class MsgSender是什么,也就不知道它是否有个sendClear函数。(备注,在g++中可以使用参数 -fpermissive参数来通过编译,但是不建议使用)

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

class CompanyZ{pubic:void sendEncryted(const std::sting& msg);……};

CompanyZ没有sendClear函数,一般性的MsgSender template对CompanyZ并不合适,这时我们可以针对CompanyZ产生一个MsgSender特化版

template<>class MsgSender<CompanyZ>{public:void sendSecret(const MsgInfo& infof){……}……};

开头的template<>表示是特化版的MsgSender template,在template实参是CompanyZ时被使用。这就是所谓的模板全特化(total template specialization):template MsgSender针对类型CompanyZ特化了,且是全面性特化,即一旦类型参数为CompanyZ,,没有其他template参数可供变化了。

再来看一下刚刚的LoggingMsgSender

template<typename Company>class LoggingMsgSender: public MsgSender<Company>{public:……//析构构造等void SendClearMsg(const MsgInfo& info){//发送前的信息写到logsendClear(info);//如实Company是CompanyZ,那么这个函数不存在//传送后信息写到log}};

如果Company=CompanyZ,那么sendClear函数就不存在。C++拒绝调用这个函数是因为它知道base class template可能被特化,而那个特化版本可能不提供和一般性template相同的接口。所以它往往拒绝在templatized base classes(模板化基类)中寻找继承而来的名称。当我们从Object Oriented C++跨进到Template C++时,继承不像以前那样畅行无阻了。

现在应该讨论一下怎么解决上面不能通过编译的问题了。我们必须有某种办法令C++“不进入templatized base class观察”的行为失效。有一下三种办法

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

template<typename Company>class LoggingMsgSender: public MsgSender<Company>{public:……//析构构造等void SendClearMsg(const MsgInfo& info){//发送前的信息写到logthis->sendClear(info);//传送后信息写到log}};

2、使用using声明式,有点类似**条款**33。

template<typename Company>class LoggingMsgSender: public MsgSender<Company>{public:……//析构构造等uinsg MsgSender<Company>::sendClear;//告诉编译器,假设sendClear位于base class内void SendClearMsg(const MsgInfo& info){//发送前的信息写到logsendClear(info);//可以编译通过,假设sendClear将被继承//传送后信息写到log}};

补充一下,这里情况和**条款**33不同,这里不是将被掩盖的base class名称带入一个derived class作用域内,而是编译器不进入base class作用域内查找,通过using告诉编译器,请它去查找。

积极的人在每一次忧患中都看到一个机会,

《Effective C++》:条款43:学习处理模板化基类内的名称

相关文章:

你感兴趣的文章:

标签云: