Множественное наследование без виртуального наследования

Я пытаюсь понять множественное наследование, вот мой код:

struct A {
  A() {}
  static int n;
  static int increment() { return ++n; }
};
int A::n = 0;

struct B : public A {};
struct C : public A {};
struct D : public B, C {};

int main() {
  D d;
  cout<<d.increment()<<endl;
  cout<<d.increment()<<endl;
}

Этот код работает. Однако, если я изменю increment() на нестатический, это не сработает.

Мои вопросы:

  • Почему компилятор жалуется на неоднозначный вызов нестатической версии increment(), но удовлетворяет статическому?
  • Если я добавлю другую функцию increment() в B или C, компилятор тоже будет жаловаться, даже объявлен как статический. Почему?

Ответ 1

Что означает двусмысленность?

Компилятор жалуется на неоднозначные вызовы, когда он не может решить, какую функцию вызывать с учетом контекста. Итак, чтобы понять жалобы, вы должны проверить, какие могут быть возможные двусмысленности.

Почему компилятор жалуется на неоднозначный вызов нестатической версии increment(), но удовлетворяет статическому?

По определению функция static класса не зависит от какого-либо экземпляра класса. Это подчеркивается тем фактом, что вы могли бы назвать его A::increment() (см., Нет экземпляра).

Проблема наследования алмазов заключается не в том, что компилятор не знает, какой код выполнить, он не знает, какой this должен предоставить (в вашем D объекте есть два A, один из которых содержит в B и один в C).

Когда вы используете функцию static A, не пропускается неявный this, поэтому нет проблемы; если вы попытаетесь использовать функцию не static, тогда компилятор не может решить, должен ли this указывать на A в B или в C, это неоднозначно.

Если я добавлю другую функцию increment() в B или C, компилятор тоже будет жаловаться, даже объявлен как статический. Почему?

В этот момент компилятор может выбрать между B::increment() и C::increment(), который он должен выбрать? Это двусмысленно.

Когда у вас есть линейная иерархия, она вызывает "ближайший" к ней (который скрывает их далее по дереву наследования), но здесь B и C являются двумя независимыми ветвями и нет "лучшей" ветки.

Примечание: даже если B не реализует increment, так как A вы можете вызвать B::increment(), который фактически вызывает A::increment(). То же самое касается C.

Ответ 2

  • Это называется проблемой наследования алмазов. У вас есть два класса, которые являются производными от A. Тогда класс получен из двух. У вас есть две версии нестатического приращения void(). Со статикой у вас есть один.
  • Поскольку вы можете, но никогда не должны переопределять не виртуальную функцию, и, возможно, ваш компилятор является строгим или строгим.

Наследование диаманта: http://www.parashift.com/c++-faq/mi-diamond.html Функции скрытия: http://www.parashift.com/c++-faq/hiding-inherited-public.html

Ответ 3

Это утверждение:

struct D : public B, C {};

Создал бы два экземпляра A. Компилятор должен знать, из какого экземпляра вы намереваетесь вызвать метод. Функция static - это не что иное, как глобальная функция, которая является дружественной к классу - единственное требование - это полное имя, когда собеседник намерен его вызвать. Static не будет играть никакой роли в наследовании.