Effective C++ (E3 43)笔记之学会处理模板化基类内的名称

有个程序要传送信息到不同公司,信息可能是密码也可以能使未经处理的。传送的不同公司的代码不同,在编译期间我们有足够信息决定传送到哪一个公司,于是采用基于template的解法:

class CompanyA{
public:
	void sendClrText(const std::string& str)
	{
		cout<<"compA::sendClrText==>"<<str.data()<<endl;
	}
	void sendEncrypted(const std::string& str)
	{
		cout<<"compA::sendEncrypted==>"<<str.data()<<endl;
	}

};

class CompanyB{
public:
	void sendClrText(const std::string& str)
	{
		cout<<"compB::sendClrText==>"<<str.data()<<endl;
	}
	void sendEncrypted(const std::string& str)
	{
		cout<<"compB::sendEncrypted==>"<<str.data()<<endl;
	}
};


class MsgInfo{
public:
	MsgInfo(const std::string& msg)
		:rmsg(msg)
	{}
	enum MSGLEVEL{	//declaration must add ";" at end, defination cannt add ";"
		NON = 0,
		ENC,
	};
	std::string createMsg(MSGLEVEL lev) const
	{
		std::string tmp;
		switch(lev)
		{
			case NON:
				tmp = rmsg;
				break;
			case ENC:
				tmp = "******"+rmsg+"******";
				break;
			default:
				break;
		}
		return tmp;
	}
	std::string rmsg;
};

template<typename Company>
class MsgSender
{
public:
	void sendClear(const MsgInfo& info){
		std::string msg;
		msg = info.createMsg(MsgInfo::NON);
		Company c;
		c.sendClrText(msg);
	}
	void sendSecret(const MsgInfo& info){
		std::string msg;
		msg = info.createMsg(MsgInfo::ENC);
		Company c;
		c.sendEncrypted(msg);
	}

};

这种解法可行,验证:

	MsgSender<CompanyA> senda;
	MsgInfo ma("PPPP");
	senda.sendClear(ma);
	senda.sendSecret(ma);

	MsgSender<CompanyB> sendb;
	MsgInfo mb("QQQQ");
	sendb.sendClear(mb);
	sendb.sendSecret(mb)

结果:



现在假设要在发出信息的前后做一些log,考虑用继承类来实现:

template<typename Company>
class LoggingMsgSender:public MsgSender<Company>
{
public:
	void sendClearMsg(const MsgInfo& info){
		cout<<"logging start"<<endl;
		//sendClear(info);	//err,compiler doesnt know MsgSender<company> whether has thisfunc or not
		cout<<"logging end"<<endl;
	}
	void sendSecretMsg(const MsgInfo& info){
		cout<<"logging start"<<endl;
		//sendSecret(info);
		cout<<"logging end"<<endl;
	}

};

这里的sendClearMsg避免名称遮掩,也避免重新定义继承而得的非虚函数(好设计),但是编译器因看不到sendClear而报错。问题在于,基类是个模板,编译器无法确定具现化后到底有没有此函数。比如有个仅仅使用加密通讯的CompanyZ:

class CompanyZ{
public:
	void sendEncrypted(const std::string& str)
	{
		cout<<"compZ::sendEncrypted==>"<<str.data()<<endl;
	}
};
由于一般的MsgSender模板提供了发送原始信息的接口,其不适用于CompanyZ。因此针对CompanyZ将MsgSender进行全特化(此MsgSender参数完全确定,已经不是模板):

template<>	//total template specialization
class MsgSender<CompanyZ>
{
public:
	void sendSecret(const MsgInfo& info){
		std::string msg;
		msg = info.createMsg(MsgInfo::ENC);
		CompanyZ c;
		c.sendEncrypted(msg);
	}
};

再次考虑继承类,因为当基类被注定为MsgSender<CompanyZ>时,没有sendClear函数,自然就不合法了!因为 编译器知道基类模板可能被特化,而那个特化版本却和一般版本接口不一致,它拒绝在模板化基类中寻找继承而来的名称。同样,即便CompanyZ有sendSecret函数,编译器也无法通过。

有三个方法使得编译器在模板化基类中寻找继承来名称:

template<typename Company>
class LoggingMsgSender:public MsgSender<Company>
{
public:
	//using MsgSender<Company>::sendClear; 	//2nd. tell compiler to find in base class scope
	void sendClearMsg(const MsgInfo& info){
		cout<<"logging start"<<endl;
		this->sendClear(info);	//1st, assume sendclear will be inheritted
		//sendClear(info);	//2nd
		//MsgSender<Company>::sendClear(info);//3rd,not good,if func is virtual,is will close virtual function
		cout<<"logging end"<<endl;
	}
	void sendSecretMsg(const MsgInfo& info){
		cout<<"logging start"<<endl;
		this->sendSecret(info);
		cout<<"logging end"<<endl;
	}

};

其一是 在函数调用时加上“this->”,假设sendClear将被继承

其二是使用using声明式,告诉编译器,请它去基类中寻找sendClear

其三是直接指出调用基类中的sendClear。(此法最不好,如果sendClear是虚函数,它将会关闭虚函数的绑定行为)

不论哪一种方法,都是对编译器承诺任何模板化基类的任何特化版本都支持一般(泛化)版本所提供接口。但这个承诺没有被实践出来,往后编译依旧报错:

	LoggingMsgSender<CompanyZ> logsendz;//2nd will err, using...
	MsgInfo lmz("lmz");
	logsendz.sendClearMsg(lmz);	//err whichever method is used

这么说,面对基类模板中可能性出现的无效接口,以上所讨论的只不过是将编译错误延后而已。C++准则是宁愿较早诊断,这也是为何编译器不去模板化基类中寻找名称的缘故。


PS:用using声明式的方法时,实例化 CompanyZ类型LoggingMsgSender的时候也会报错(当然CompanyA、CompanyB类型没问题):


可见using方法,会使编译器在模板实例化时,优先查找其基类的CompanyZ特化版本内IF是否有效。否则拒绝实例化而不是类似于方法一、三直到函数调用才报错(它们是假定IF肯定被实践出来了)。哈哈,这也算将稍将编译错误提前侦测出来了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值