template<typename Company>
class MsgSender
{
public:
void sendClear(const MsgInfo& info)
{
...
}
void sendSecret(const MsgInfo& info)
{
...
}
};
template<typename Company>
class LoggingMsgSender:public MsgSender<Company>
{
public:
void sendClearMsg(const MsgInfo& info)
{
sendClear(info);
}
};
上述代码无法通过编译,编译器会抱怨sendClear不存在,但是我们明明看到sendClear的确在base class内,编译器却看不到它,为什么?
问题在于,当编译器遭遇class template LoggingMsgSender定义式时,并不知道它继承什么样的class。当然它继承的是MsgSender<Company>,但其中Company是个template参数,不到LoggingMsgSender被具现化无法确定它是什么。而如果不知道Company是什么,就无法知道class MsgSender<Company>看起来像什么——更明确地说没办法知道它是否有个sendClear函数。
为了更清楚说明问题,假设我们有个class CompanyZ:
class CompanyZ
{
public:
void sendEncrypted(const std::string& msg);
}
一般的MsgSender template对CompanyZ不合适,因为CompanyZ不需要sendClear函数,所以针对CompanyZ产生一个MsgSender特化版:
template<>
class MsgSender<CompanyZ>
{
public:
void sendSecret(const MsgInfo& info)
{
...
}
}
class定义式前面的template<>语法象征这既不是template也不是标准class而是个特化版的MsgSender template,在template实参是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,这个函数不存在
}
};
正如注释所言,当baseclass 被指定为MsgSender<CompanyZ>时,这段代码不合法,因为那个class并未提供sendClear函数。那就是为什么C++拒绝这个调用的原因:它知道base class template有可能被特化,而那个特化版本可能不提供和一般性template相同的接口。因此它往往拒绝在模板化的基类(MsgSender<Company>)内寻找继承而来的名称(sendClear)。
解决办法有三种:
(1)使用this->
template<typename Company>
class LoggingMsgSender:public MsgSender<Company>
{
public:
void sendClearMsg(const MsgInfo& info)
{
this->sendClear(info); //成立,假设sendClear被继承
}
};
(2)使用using
template<typename Company>
class LoggingMsgSender:public MsgSender<Company>
{
public:
using MsgSender<Company>::sendClear;//告诉编译器,假设sendClear位于base class内
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>::sendClear(info);
}
};
但这往往是最不让人满意的一个解法,因为如果被调用的是virtual函数,上述的明确资格修饰会关闭“virtual绑定行为”。