Неоднозначное наследование функции, когда множественное наследование классов, которые сами имеют алмазное наследование в своей иерархии

Описание слова (код ниже): У меня есть библиотека, которая предоставляет коллекцию классов. Для каждой группы классов у нас есть два конкретных типа, (ClassA_Partial, ClassA), (ClassB_Partial, ClassBи т. д. Каждый из этих инструментов (Interface_Partial, Interface) соответственно. Дополнительно, Interface это Interface_Partial и каждый Class? это Class?_Partial — создание шаблона наследования алмазов, при котором вершина наследуется виртуально.

ПочемуInterface_Partial функции неоднозначны при наследовании обоих ClassA а также ClassB?

struct Interface_Partial
{
virtual ~Interface_Partial();
virtual void f() = 0;
};

struct Interface
:
virtual Interface_Partial
{
virtual void g() = 0;
};struct ClassA_Partial : public virtual Interface_Partial
{
void f() {};
};struct ClassA : public Interface, public virtual ClassA_Partial
{
void g() {};
};

struct ClassB_Partial : public virtual Interface_Partial
{
void f() {};
};struct ClassB : public Interface, public virtual ClassB_Partial
{
void g() {};
};

struct MyClass : public ClassA, public ClassB
{ }; // error C2250: MyClass : ambiguous inheritance of 'void Interface_Partial::f(void)'

Почему мы не можем избавиться от неоднозначности, как обычно, когда мы наследуем общий интерфейс более одного раза? Например

struct ClassX : public Interface_Partial { void f() {} };
struct ClassY : public Interface_Partial { void f() {} };
class Another : public ClassX, public ClassY
{};

void func()
{
// This is ok
Another a;
a.ClassX::f();

// Why would this not work?
// unambiguously refers to the one and only f() function
// inherited via  ClassA
MyClass b;
b.ClassA::f();
}

4

Решение

Из-за виртуального наследования существует только одна vtable для базового класса Interface_Partial — как только вы используете виртуальное наследование, «виртуальность» заражает все производные классы на всех уровнях

Наследство неоднозначно, потому что MyClass доступно две разные версии f () — одна из ClassA и один из ClassB, Из-за виртуального наследования Interface_Partialу вас есть две реализации производного класса, которые находятся на одном уровне и пытаются переопределить одну и ту же виртуальную функцию. Объявление виртуального базового класса делает все производные классы общими виртуальным базовым классом, включая его vtable. Общая виртуальная таблица обновляется, чтобы содержать указатель виртуальной функции, которая должна быть вызвана. Но так как есть два одинаково «хороших» на выбор, нет способа выбрать одно из другого.

В другом примере вы даете, Interface_Partial это не виртуальный базовый класс для ClassX а также ClassYтаким образом, каждый класс переопределяет совершенно другую виртуальную функцию. Это однозначно для компилятора, хотя, когда вы вызываете их, вы должны указать, какие конкретные f() ты хочешь позвонить.

Вы можете решить эту проблему, обеспечив реализацию f() в MyClass,

2

Другие решения

Чтобы ответить на ваш вопрос, нам нужно понять работу виртуальной функции.

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

введите описание изображения здесь

За Interface_Partial: Это Vtable будет содержать адрес функции f () со своим собственным определением, т.е. Interface_Partial :: f ().

В Интерфейс класс / структура: у него есть функция g () со своим собственным определением вместе с унаследованной функцией f () от Interface_Partial, которую она не переопределяет. Так что Vtable of Интерфейс класс будет иметь адрес двух функций:

  1>    g() as  Interface :: g()

2>    f() as  Interface_Partial :: f()

Теперь приходите ClassA_Partial : Он переопределил функцию f () из родительского класса и дал собственное определение, поэтому таблица ClassA_Partial будет иметь функции f () как:

ClassA_Partial :: f()

Теперь основная часть:

Если вы видите ClassA он унаследовал от Интерфейс а также ClassA_Partial и переопределяет g (), но не f (). Поэтому, когда компилятор увидит это, он будет сбит с толку, потому что теперь он будет иметь два определения f ()

 1> as   Interface_Partial :: f()
2> as   ClassA_Partial :: f()

Какой выбрать? Interface_Partial или ClassA_Partial! Так что выдает ошибку, даже если вы делаете как

 b.ClassA::f();

Из-за двух разных версий f ()

4

По вопросам рекламы [email protected]