#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 однозначен.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Какие существуют виды рекламных бордов и как выбрать подходящий?
Почему при выполнении кода оба массива постоянно инициализируются (или, по крайней мере, выводятся на экран) одинаковыми числами? https://wwwonlinegdb
Дана задача, в которой надо ввести числа в массив, при этом, вводить их надо через пробел