日期:2011-03-22 13:55:00 来源:本站整理
C++箴言:若何拜候模板化基类中的名字[VC/C++编程]
本文“C++箴言:若何拜候模板化基类中的名字[VC/C++编程]”是由七道奇为您精心收集,来源于网络转载,文章版权归文章作者所有,本站不对其观点以及内容做任何评价,请读者自行判断,以下是其具体内容:
假定我们要写一个利用程序,它可以把消息传送到几个差别的公司去.消息既可以以加密方法也可以以明文(不加密)的方法传送.假如我们有充足的信息在编译期间肯定哪个消息将要发送给哪个公司,我们便可以用一个 template-based(模板基)来办理问题:
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 for other companies class MsgInfo { ... }; // class for holding information // used to create a message template<typename Company> class MsgSender { public: ... // ctors, dtor, etc. void sendClear(const MsgInfo& info) { std::string msg; create msg from info; Company c; c.sendCleartext(msg); } void sendSecret(const MsgInfo& info) // similar to sendClear, except { ... } // calls c.sendEncrypted }; |
这个可以很好地工作,但是假定我们有时需求在每次发送消息的时刻把一些信息记录到日记中.通过一个 derived class(派生类)可以很简单地增添这个功效,下面这个仿佛是一个公道的办法:
template<typename Company> class LoggingMsgSender: public MsgSender<Company> { public: ... // ctors, dtor, etc. void sendClearMsg(const MsgInfo& info) { write "before sending" info to the log; sendClear(info); // call base class function; // this code will not compile! write "after sending" info to the log; } ... }; |
注意 derived class(派生类)中的 message-sending function(消息发送函数)的名字 (sendClearMsg) 与它的 base class(基类)中的那个(在那边,它被称为 sendClear)差别.这是一个好的计划,因为它避开了 hiding inherited names(躲藏担当来的名字)的问题(拜见《C++箴言:避免覆盖通过担当得到的名字》)和重定义一个 inherited non-virtual function(担当来的非虚拟函数)的与生俱来的问题(拜见《C++箴言:毫不重定义担当的非虚拟函数》).但是上面的代码不能通过编译,至少在符合尺度的编译器上不能.这样的编译器会抱怨 sendClear 不存在.我们可以瞥见 sendClear 就在 base class(基类)中,但编译器不会到那边去探求它.我们有必要理解这是为什么.
问题在于当编译器碰到 class template(类模板)LoggingMsgSender 的 definition(定义)时,它们不知道它从哪个 class(类)担当.当然,它是 MsgSender<Company>,但是 Company 是一个 template parameter(模板参数),这个直到更迟一些才能被肯定(当 LoggingMsgSender 被实例化的时刻).不知道 Company 是什么,就没有办法知道 class(类)MsgSender<Company> 是什么模样的.分外是,没有办法知道它能否有一个 sendClear function(函数).
为了使问题具体化,假定我们有一个要求加密通讯的 class(类)CompanyZ:
class CompanyZ { // this class offers no public: // sendCleartext function ... void sendEncrypted(const std::string& msg); ... }; |
普通的 MsgSender template(模板)不实用于 CompanyZ,因为那个模板供应一个 sendClear function(函数)关于 CompanyZ objects(对象)没有意义.为了改正这个问题,我们可以成立一个 MsgSender 针对 CompanyZ 的特化版本:
template<> // a total specialization of class MsgSender<CompanyZ> { // MsgSender; the same as the public: // general template, except ... // sendCleartext is omitted void sendSecret(const MsgInfo& info) { ... } }; |
注意这个 class definition(类定义)开始处的 "template <>" 语法.它表示这既不是一个 template(模板),也不是一个 standalone class(独立类).精确的说法是,它是一个用于 template argument(模板参数)为 CompanyZ 时的 MsgSender template(模板)的 specialized version(特化版本).这以 total template specialization(完好模板特化)闻名:template(模板)MsgSender 针对范例 CompanyZ 被特化,并且这个 specialization(特化)是 total(完好)的——只要 type parameter(范例参数)被定义成了 CompanyZ,就没有剩下能被改变的别的 template's parameters(模板参数).
已知 MsgSender 针对 CompanyZ 被特化,再次考虑 derived class(派生类)LoggingMsgSender:
template<typename Company> class LoggingMsgSender: public MsgSender<Company> { public: ... void sendClearMsg(const MsgInfo& info) { write "before sending" info to the log; sendClear(info); // if Company == CompanyZ, // this function doesn't exist! write "after sending" info to the log; } ... }; |
就像注释中写的,当 base class(基类)是 MsgSender<CompanyZ> 时,这里的代码是无意义的,因为那个类没有供应 sendClear function(函数).这就是为什么 C++ 回绝这个调用:它承认 base class templates(基类模板)可以被特化,而这个特化不一定供应和 general template(通用模板)相同的 interface(接口).后果,它普通会回绝在 templatized base classes(模板化基类)中探求 inherited names(担当来的名字).在某种意义上,当我们从 Object-oriented C++ 超越到 Template C++,inheritance(担当)会终止工作.
为了重新启动它,我们必须以某种方法使 C++ 的 "don't look in templatized base classes"(不在模板基类中探求)行为失效.有三种办法可以做到这一点.首先,你可以在调用 base class functions(基类函数)的前面加上 "this->":
template<typename Company> class LoggingMsgSender: public MsgSender<Company> { public: ... void sendClearMsg(const MsgInfo& info) { write "before sending" info to the log; this->sendClear(info); // okay, assumes that // sendClear will be inherited write "after sending" info to the log; } ... }; |
第二,你可以利用一个 using declaration,假如你已经读过《C++箴言:避免覆盖通过担当得到的名字》,这应当是你很熟习的一种办理筹划.该文注释了 using declarations 若何将被躲藏的 base class names(基类名字)引入到一个 derived class(派生类)范畴中.因此我们可以这样写 sendClearMsg:
template<typename Company> class LoggingMsgSender: public MsgSender<Company> { public: using MsgSender<Company>::sendClear; // tell compilers to assume ... // that sendClear is in the // base class void sendClearMsg(const MsgInfo& info) { ... sendClear(info); // okay, assumes that ... // sendClear will be inherited } ... }; |
(固然 using declaration 在这里和《C++箴言:避免覆盖通过担当得到的名字》中都可以工作,但要办理的问题是差别的.这里的情形不是 base class names(基类名字)被 derived class names(派生类名字)躲藏,而是假如我们不奉告它去做,编译器就不会搜索 base class 范畴.)
最后一个让你的代码通过编译的办法是显式指定被调用的函数是在 base class(基类)中的:
template<typename Company> class LoggingMsgSender: public MsgSender<Company> { public: ... void sendClearMsg(const MsgInfo& info) { ... MsgSender<Company>::sendClear(info); // okay, assumes that ... // sendClear will be } // inherited ... }; |
普通这是一个办理这个问题的最不合人心的办法,因为假如被调用函数是 virtual(虚拟)的,显式限定会关闭 virtual binding(虚拟绑定)行为.
从名字可见性的概念来看,这里每一个办法都做了一样的事情:它向编译器保证任何后继的 base class template(基类模板)的 specializations(特化)都将支持 general template(通用模板)供应的 interface(接口).全部的编译器在解析一个像 LoggingMsgSender 这样的 derived class template(派生类模板)是,这样一种保证都是必要的,但是假如保证被证实不成立,本相将在后继的编译历程中表露.比方,假如背面的源代码中包含这些,
LoggingMsgSender<CompanyZ> zMsgSender; MsgInfo msgData; ... // put info in msgData zMsgSender.sendClearMsg(msgData); // error! won't compile |
对 sendClearMsg 的调用将不能编译,因为在目前,编译器知道 base class(基类)是 template specialization(模板特化)MsgSender<CompanyZ>,它们也知道那个 class(类)没有供应 sendClearMsg 试图调用的 sendClear function(函数).
从根本上说,问题就是编译器是早些(当 derived class template definitions(派生类模板定义)被解析的时刻)诊断对 base class members(基类成员)的不法引用,还是晚些时刻(当那些 templates(模板)被特定的 template arguments(模板参数)实例化的时刻)再举行.C++ 的方针是甘愿早诊断,而这就是为什么当那些 classes(类)被从 templates(模板)实例化的时刻,它假充不知道 base classes(基类)的内容.
Things to Remember
·在 derived class templates(派生类模板)中,可以经过 "this->" 前缀引用 base class templates(基类模板)中的名字,经过 using declarations,或经过一个 explicit base class qualification(显式基类限定).
以上是“C++箴言:若何拜候模板化基类中的名字[VC/C++编程]”的内容,如果你对以上该文章内容感兴趣,你可以看看七道奇为您推荐以下文章:
本文地址: | 与您的QQ/BBS好友分享! |
评论内容只代表网友观点,与本站立场无关!
评论摘要(共 0 条,得分 0 分,平均 0 分)
查看完整评论