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

(一)

注意从 “面向对象的C++” 转向 “模板C++” 时继承可能遭遇问题 :由于基类模板可能被特化,而该特化版本可能会改变成员,因此C++拒绝在模板化基类中寻找继承而来的名称。


(二)

看下面的例子:

假设将信息传送到不同的公司去,传送方式包括明文传送和密文传送,采用模板类的设计方法:

class CompanyA {
public:
	...
	void sendClearText(const string& msg);
	void sendEncrypted(const string& msg);
	...
};

class CompanyB {
public:
	...
	void sendClearText(const string& msg);
	void sendEncrypted(const string& msg);
};

...//其他公司的classes

class MsgInfo {...}; //这个class为将来生产保存信息

template<typename Company>
class MsgSender {
public:
	...
	void sendClear(const MsgInfo& info) {
		string msg;
		...//根据info产生信息
		Company c;
		c.sendClearText(msg);
	}
	void sendSecret(const MsgInfo& info) {...}//这里调用的是c.sendEncrypted
};
如果想要添加发送信息时记录信息的功能,采用继承的方式:

template<typename Company>
class LoggingMsgSender : public MsgSender<company> {
public:
	...
	void sendClearMsg(const MsgInfo& info) {
		...//将传送前的信息写至记录信息
		sendClear(info);//试图调用基类成员函数完成发送,但是不能通过编译。这是因为在派生类没有具体化时,不知道Company是什么类型,也就不知道基类中是否存在该函数
		...//将传送后的信息写至记录信息
	}
	...
};
        编译器不让其通过编译.此时的编译器抛出了"sendClear不存在"的抱怨.可sendClear明显就在base class内部啊?郁闷,这是为什么倪?别急,我来解释一下:当编译器遇到LoggingMsgSender定义式时,由于Company是一个template参数,不到LoggingMsgSender被具现化的时候,编译器是无法知道Company是什么的.由于无法知道Company是什么,那么它就无法确定MsgSender是什么,也就无法获知其否有个sendClear函数. 停一下,你可能现在对我的解释会有一个问题:为什么根据template Company就不能确定Company是什么了呢?template Company不是有声明式嘛!在这里我提出:我们不能根据一般性模板类的声明式来确定具现化类具有的操作,因为模板类有个特化声明版本的问题.

(三)

改进方法:

(1)在基类函数调用动作之前加上this指针:

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

(2)使用using声明式在类内使用基类命名空间:

template <typename Company>
     class LoggingMsgSender:public MsgSender<Company>{
     public:
         //这里的情况不是base class名称被derived class名称遮掩,而是编译器不进入base base
         //作用域查找,于是我们通过using声明式告诉它,请它这么做
         using MsgSender<Company>::sendClear;//告诉编译器,请它假设sendClear位于base class内
         ...
         void sendClearMsg(const MsgInfo& info){
             ...
             sendClear(info);//ok
             ...
         }
     };

(3)明确限定函数是基类中的函数:

 template <typename Company>
     class LoggingMsgSender:public MsgSender<Company>{
     public:
         ...
         void sendClearMsg(const MsgInfo& info){
             ...
             MsgSender<Company>::sendClear(info); //ok
             ...
         }
         ...
     };
不过此方法有一个很明显的暇疵:如果被调用的是virtual函数,上述的明确资格修饰会关闭"virtual绑定行为".
所以还是尽量选择前两种方法吧。


请记住:

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







郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。