只是一个小小的烦恼,因为我可以通过包装派生函数而不是使用'using'关键字解决问题,但为什么不执行以下工作(编译器告诉我''get_elem'仍然是'Bar'中的纯虚拟类).
class Elem {};
class DerivedElem : public Elem {};
class Foo {
public:
virtual Elem& get_elem() = 0;
};
class Goo {
protected:
DerivedElem elem;
public:
DerivedElem& get_elem() { return elem; }
};
class Bar : public Foo, public Goo {
public:
using Goo::get_elem;
};
int main(void) {
Bar bar;
}
Run Code Online (Sandbox Code Playgroud)
干杯,
汤姆
如果Goo是一个"mixin",旨在以特定方式实现接口Foo(可能还有其他mixins与其他实现),那么Goo可以从Foo派生(而不是Bar这样做).
如果Goo不是为了实现接口Foo而设计的,那么将Bar视为已经实现了纯虚函数将是一个可怕的错误,而事实上它恰好具有相同签名的功能.如果你想要隐式接口和C++中的"duck"类型,你可以做到,但你必须使用模板.无论是对还是错,纯虚函数都是用于显式声明的接口,并且get_elem没有显式声明Goo的函数实现Foo::get_elem.所以它没有.
我想这并没有解释为什么原则上语言无法using Goo::get_elem for Foo;在Bar中定义,或者在Bar中有一些这样的声明,以避免Bar需要包含很多包含调用的样板.
您可以使用模板执行某些操作以允许Goo在某种程度上支持此操作,而无需真正了解Foo:
template <typename T>
class Goo : public T {
protected:
DerivedElem elem;
public:
DerivedElem& get_elem() { return elem; }
};
class Bar : public Goo<Foo> {};
class Baz : public Goo<Fuu> {};
Run Code Online (Sandbox Code Playgroud)
Fuu是其他具有get_elem功能的界面.显然,作者有责任Bar确保Goo真正履行合同Foo,并且同样Baz检查合同Fuu.
顺便说一句,这种形式的协方差有点狡猾.看看Foo,有人可能希望表达式bar.get_elem() = Elem()有效,而不是,因此违反了LSP.参考文献很有趣.((Foo &)bar).get_elem() = Elem()是有效但一般不起作用!它只分配给Elem子对象,就此而言((Foo &)bar).get_elem() = DerivedElem().多态分配基本上是一种麻烦.
在您的示例中,Foo 和 Goo 是单独的类。在 Bar 中,Goo 中的 get_elem 方法与 Foo 中的方法完全不同,即使它们的签名匹配。
通过使用using Goo::get_elem,您只需告诉编译器将对 get_elem() 的非限定调用解析为 Goo 中的调用。