Почему чисто виртуальные методы не переопределяются из другой ветви наследования?

У меня есть, может быть, немного сложная иерархия классов:

class BS {
  public:
    virtual void meth()=0;
};

class BCA : public virtual BS {
};

class BSS : public virtual BS {
};

class BCS : public virtual BCA, public virtual BSS {
};

class BI4 {
  public:
    void meth() {};
};

class BT4 : public virtual BI4, public virtual BSS {
};

class T4 : public virtual BCS, public virtual BT4 {
};

int main() {
  T4 t4;
};

Теперь проблема в том, что хотя void meth() доступно в графе наследования, несмотря на то, что этот код не будет компилироваться:

$ g++ -c t.cc -std=c++11
t.cc: In function ‘int main()’:
t.cc:27:6: error: cannot declare variable ‘t4’ to be of abstract type ‘T4’
   T4 t4;
      ^
t.cc:23:7: note:   because the following virtual functions are pure within ‘T4’:
 class T4 : public virtual BCS, public virtual BT4 {
       ^
t.cc:3:18: note:        virtual void BS::meth()
     virtual void meth()=0;
                  ^
t.cc:3:18: note:        virtual void BS::meth()

Мне кажется как будто BS как-то не увидит перегруженного meth() метод через цепочку BS->BCA->BCS->T4->BT4->BI4.
Но почему? Этот метод доступен, алгоритм линеаризации C3, используемый C++, должен очень четко его найти.

3 ответа

Решение

Есть два основных аспекта:

  • Данный класс может переопределять функции-члены только из своих базовых классов.
    Так как ваш BI4 класс не имеет BS как базовый класс, он не может переопределить что-либо из BS,
  • Можно реализовать в реализации чисто виртуальной функции, определенной в виртуальном базовом классе, во многом как в Java, но сам класс, обеспечивающий эту реализацию, также должен иметь этот виртуальный базовый класс.

Пример:

struct Base
{
    virtual void foo() = 0;
};

#ifdef GOOD
    struct Impl_foo: virtual Base
    {
        void foo() override {}
    };
#else
    struct Impl_foo
    {
        virtual void foo() {}
    };
#endif

struct Abstract_derived: virtual Base
{};

struct Derived
    : Abstract_derived
    , Impl_foo      // Java-like implementation inheritance.
                    // In C++ called "by dominance".
{};

auto main()
    -> int
{
    Derived o;
    o.foo();
}

Без определения GOOD символ макроса, этот код не компилируется.

Правила языка не позволяют этого. Виртуальная функция может быть переопределена только объявлением функции с тем же именем и параметрами в производном классе. поскольку BI4 не выводится из BS, BI4::meth не может переопределить BS::meth, Если класс наследует (прямо или косвенно) от обоих BS а также BI4то он наследует две функции meth: один из BS, все еще абстрактный и не переопределенный, и один из BI4,

BI4 не наследуется от BS прямо или косвенно, поэтому его метод BI4::meth() совершенно не связан и не может переопределить BS::meth(),

Вы можете переопределять методы только из базовых классов, а не из классов "одноуровневых" или "дядя".

Другие вопросы по тегам