#include <iostream>
#include <string>
class A {
public:
virtual void print()const {
std::cout << "from A" << std::endl;
}
};
class B : public A {
public:
void print()const override{
std::cout << "from B" << std::endl;
}
};
class C : public A {
public:
void print()const override{
std::cout << "from C" << std::endl;
}
};
class D :public B, public C {
public:
using C::print;
};
int main()
{
D d{};
d.print(); //from B
return 0;
}
И почему нельзя использовать объявление using при виртуальном наследовании?
Если речь идет о Visual Studio, то это, по-видимому, баг компилятора.
Кодогенератор Visual Studio по какой-то причине принимает решение в такой ситуации реализовать вызов d.print() как полноценный виртуальный вызов через таблицу виртуальных функций класса D. (В этом, кстати, пока еще нет никакой ошибки.) Но для разрешения этого виртуального вызова он безусловно берет первую таблицу в объекте типа D (из первой базы в списке баз), по каковой причине такие виртуальные вызовы все время попадают в B::print. Если поменять местами базы в объявлении класса D, то вызываться все время будет C::print.
Если сделать функцию print невиртуальной, то проблема пропадает. Если явно сделать вызов невиртуальным, т.е. вместо d.print() вызывать d.D::print(), то проблема пропадает.
Это вообще-то интересная тема. Вызов d.print() - это с точки зрения языка виртуальный вызов и разрешаться он должен на основе динамического типа объекта d. Т.е. final overrider должен быть взят из класса D. А кто является final overrider в D? (Точнее, из какой цепочки баз он должен выбираться: A->B->D или A->С->D?). Using-declaration не может быть использована для разрешения этого вопроса. В стандарте есть показательный пример на эту тему
struct A {
virtual void f();
};
struct B : virtual A {
virtual void f();
};
struct C : B , virtual A {
using A::f;
};
void foo() {
C c;
c.f(); // calls B::f, the final overrider
c.C::f(); // calls A::f because of the using-declaration
}
Обратите внимание - c.f() должен вызывать B::f() несмотря на присутствие using A::f в C.
По-видимому в нашем примере все начинается c name lookup, который должен использовать using-declaration using C::print и локализовать рассмотрение в ветке наследования A->C, где final overrider однозначен.
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости