假设我们需要撰写一个程序,它能够传送信息到若干不同的公司去:
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);
};
// ... 针对其他公司设计的classes
class MsgInfo {}; // 用来保存信息,以备将来产生信息s
template<typename Company>
class MsgSender {
public:
//.. 构造,析构函数等
void sendClear(const MsgInfo& info) {
std::strin msg;
//... 产生info信息
Company c;
c.sendClearText(msg);
}
void sendSecret(const MsgInfo& info) { // 类似sendClear
}
};
现在假如我们想在每次发送出信息时产生日志(log)用来记录
,derived class
可轻易做到,如下:
template<typename Company>
class LogginMsgSender : public MsgSender<Company> {
public:
//...
void sendCleaMsg(const MsgInfo& info) {
//... 将"传送前"的信息写到log
sendClear(info); // 调用base class函数,这里无法通过编译
//... 将"传送后"的信息写到log
}
};
上述代码无法通过编译是因为当编译器遇到class template, LoggingMsgSender
,并不知道它继承什么样的class
当然它继承的是MsgSender<Company>
,但是其中的Company
是个template
参数,不到LogginMsgSender
被具现化时无法确切知道它是什么,就无法知道它是否有个sendClear
函数
为了让问题更具体化,假设我们有一个class CompanyZ
只使用加密通讯:
class CompanyZ {
public:
// ...
void sendEncrypTed(const std::string &msg);
};
一般性的MsgSender template
对CompanyZ
并不合适,因为那个template
提供了一个sender
函数;要解决这个问题我们可以产生一个MsgSender
特化版:
template<>
class MsgSender<CompanyZ> {
public:
//...
void senScret(const MsgInfo& info){
// ...
}
};
template<>
这个语法既不是template
也不是标准的class
,而是一个特化版的MsgSender template
,在template
实参是CompanyZ
时候被使用
现在,MsgSender
针对CompanZ
进行了全特化,当再次运行derived LoggingMsgSender
时,当base class
被制定为MsgSender<CompanyZ>时``sendClear
函数依旧报错,因为CompanyZ
未提供sendClear
函数,解决办法有三种:
- 在
base class
函数调用动作之前加this->
:this->sendClear
- 使用
using
声明式:
template<typename Company>
class LogginMsgSender : public MsgSender<Company> {
public:
//...
using MsgSender<Company>::sendClear;
void sendCleaMsg(const MsgInfo& info) {
//... 将"传送前"的信息写到log
sendClear(info); // 调用base class函数,这里无法通过编译
//... 将"传送后"的信息写到log
}
};
- 明确指出被调用的函数位于
base class
内:(这是最不好的一种方法,因为如果被调用的是virtual
函数,上述代码的明确调用会关闭"virtual"绑定行为)
template<typename Company>
class LogginMsgSender : public MsgSender<Company> {
public:
//...
using MsgSender<Company>::sendClear;
void sendCleaMsg(const MsgInfo& info) {
//... 将"传送前"的信息写到log
MsgSender<Company>::sendClear(info); // 调用base class函数,这里无法通过编译
//... 将"传送后"的信息写到log
}
};
当main
函数里面产生如下实际调用时候:
LogginMsgSender<CompanyZ> zMsgSender;
MsgInfo msgData;
zMsgSender.sendCleaMsg(msgData);
其中对sendClearMsg
的调用将无法通过编译,因为编译器知道base class
是一个template
特化版本MsgSender<CompanyZ>
,而它们知道那个class
不提供sendClear
函数,而后者却是sendClearMsg
尝试调用的函数