В приведенном ниже коде я получаю следующее предупреждение и ошибку:
test.cpp:15: warning: direct base 'B' inaccessible in 'D' due to ambiguity
test.cpp:15: error: no unique final overrider for 'virtual void A::f()' in 'D'
Но если я уберу виртуальное наследование B из A (т.е. struct B : public A
), Я получаю только предупреждение, без ошибок.
struct A
{
virtual void f() = 0;
};
struct B : public virtual A
{
void f() {}
};
class C : public B
{};
struct D : public C, virtual B
{};
int main()
{
return 0;
}
Зачем? Это страшный бриллиант?
Это потому что C
наследует не виртуальным образом от B
в то время как D
наследует виртуально от B
, Это дает вам B
два раза, включая два f()
,
Попробуйте виртуальное наследование B
в C
,
Обновление: так почему это работает, когда вы удаляете виртуальное наследование в B
от A
? Потому что это меняет «окончательный переопределение». Без виртуального входа B
от A
И в C
от B
у тебя есть A
два раза: один раз в C
(с окончательным переопределением f()
в B
) и один раз в виртуал B
в D
(с окончательным переопределением f()
в B
). Если вы добавите обратно виртуальное наследование в B
в A
, A
будет присутствовать только один раз, и будет два финальных переопределения, конкурирующих за реализацию чистого f()
от A
, оба в B
однажды из C
и один раз из виртуального B
,
В качестве обходного пути вы можете добавить using
до D, то есть using C::f;
или же using B::f
,
См. C ++ 10.3 / 2
Давайте посмотрим на определение «окончательного переопределения» из 10.3[class.virtual]/2
Виртуальная функция-член
C::vf
объекта классаS
является окончательным переопределением, если самый производный класс которогоS
подобъект базового класса (если есть) объявляет или наследует другую функцию-член, которая переопределяетvf
,В производном классе, если виртуальная функция-член подобъекта базового класса имеет более одного окончательного переопределения, программа некорректна.
При виртуальном наследовании от A существует только один подобъект базового класса типа A, а его виртуальная функция-член f () имеет более одного окончательного переопределения (по одному в каждом подобъекте типа B)
Без виртуального наследования от A есть два разных подобъекта базового класса типа A, и у каждого из их виртуальных функций-членов f () есть свой конечный переопределитель (по одному в каждом подобъекте B)
Виртуальные базовые подобъекты являются «общими» для всех базовых подобъектов в целостном объекте. Поскольку A является общим для D :: C :: B и D :: B, он не может сказать, какой объект B должен иметь его f()
называется переопределением для A::f().
Рассматривать:
#include <iostream>
struct A {
virtual void f() = 0;
virtual ~A() {}
};
struct B : virtual A
{
void f() { std::cout << "B\n"; }
};
struct C : virtual A
{
void f() { std::cout << "C\n"; }
};
struct D : C, B {};
int main() {
D d;
A *a = dynamic_cast<A*>(&d); // single shared A between B and C
a->f(); // Should B::f() be called, or C::f()?
}
Базовые подобъекты B и C в D совместно используют один и тот же базовый подобъект A. Когда мы вызываем A :: f (), выполняется виртуальный поиск для переопределяющей функции. Но и B, и C пытаются переопределить это, так какой из них «выигрывает»? Есть ли x->f()
печатать «B» или «C»? Ответ заключается в том, что программа, которая попадает в ситуацию, является плохо сформированной.
Когда мы исключаем совместное использование, заставляя B и C наследовать не виртуально, то у каждого отдельного базового подобъекта A свои функции переопределяются уникальными базовыми классами:
#include <iostream>
struct A {
virtual void f() = 0;
virtual ~A() {}
};
struct B : A
{
void f() { std::cout << "B\n"; }
};
struct C : A
{
void f() { std::cout << "C\n"; }
};
struct D : C, B {};
int main() {
D d;
// two different A objects
A *a1 = static_cast<A*>(static_cast<B*>(&d));
A *a2 = static_cast<A*>(static_cast<C*>(&d));
a1->f();
a2->f();
}