通过"使用"单独继承的方法覆盖纯虚函数

Tom*_*Tom 8 c++

只是一个小小的烦恼,因为我可以通过包装派生函数而不是使用'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)

干杯,

汤姆

Ste*_*sop 5

如果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().多态分配基本上是一种麻烦.


Ric*_*chN 2

在您的示例中,Foo 和 Goo 是单独的类。在 Bar 中,Goo 中的 get_elem 方法与 Foo 中的方法完全不同,即使它们的签名匹配。

通过使用using Goo::get_elem,您只需告诉编译器将对 get_elem() 的非限定调用解析为 Goo 中的调用。