Может быть, даже лучше: почему стандарт требует перенаправления в базовый класс в этих ситуациях? (да да да - почему? - потому что.)
class B1 {
public:
virtual void f()=0;
};
class B2 {
public:
virtual void f(){}
};
class D : public B1,public B2{
};
class D2 : public B1,public B2{
public:
using B2::f;
};
class D3 : public B1,public B2{
public:
void f(){
B2::f();
}
};
D d;
D2 d2;
D3 d3;
МС дает:
sourceFile.cpp
sourceFile.cpp(24) : error C2259: 'D' : cannot instantiate abstract class
due to following members:
'void B1::f(void)' : is abstract
sourceFile.cpp(6) : see declaration of 'B1::f'
sourceFile.cpp(25) : error C2259: 'D2' : cannot instantiate abstract class
due to following members:
'void B1::f(void)' : is abstract
sourceFile.cpp(6) : see declaration of 'B
и аналогично для компилятора MS.
Я мог бы купить первый случай, D. Но в D2 - f однозначно определяется объявлением using, почему этого недостаточно для того, чтобы компилятор требовал заполнения vtable?
Где в стандарте определяется эта ситуация?
добавлено в ответ на ответ
Относительно ответа ниже, который я принял:
Почему это не кажется ошибкой в спецификации? - Если у вас есть иерархия наследования с рядом не виртуальных f (), использование которых в производных классах определяется с помощью операторов, и один меняет decl of f в базовом классе на виртуальный, то это может изменить f вызывается в производных классах с использованием операторов для выбора их f Это "++", который я не знал. Это может быть частью языка, но такое «действие на расстоянии» делает меня неловким и мне кажется нарушением какого-то принципа корректности / поддержания (что я не могу сформулировать прямо сейчас).
Но я могу привести пример:
#include <iostream>
using std::cout;
namespace NonVirtual_f{
class C0 {
public:
void f(){cout<<"C0::f()"<<'\n';}
};
class C1 : public C0{
public:
void f(){cout<<"C1::f()"<<'\n';}
};
class C2 : public virtual C1{
public:
void f(){cout<<"C2::f()"<<'\n';}
};
class D3 : public virtual C1, public C2{
public:
using C1::f;
};
}//namespace NonVirtual_f
namespace Virtual_f{
class C0 {
public:
virtual void f(){cout<<"C0::f()"<<'\n';}
};
class C1 : public C0{
public:
void f(){cout<<"C1::f()"<<'\n';}
};
class C2 : public virtual C1{
public:
void f(){cout<<"C2::f()"<<'\n';}
};
class D3 : public virtual C1, public C2{
public:
using C1::f;
};
}//namespace Virtual_f
int main(int argc,const char* const*argv){
NonVirtual_f::D3 nv3;
nv3.f();
Virtual_f::D3 v3;
v3.f();
return 0;
}
Откуда вывод:
C1::f()
C2::f()
Все, что изменилось, это виртуальность f в C0. В частности, если выбрана не виртуальность f в базовом классе, она не может быть изменена без проблем с обслуживанием, если какой-то производный класс (который вообще не может знать о 1033 **1034*) "переопределено", как в приведенном выше примере.
Если вы возражаете против «Ну, не переопределяйте это в случае с не-виртуальными», я согласен, что это плохая практика, но кажется, что это нечто большее. Для меня язык должен:
не разрешать использование в NonVirtual :: D3 (в настоящее время невозможно, так как могут быть введены другие перегруженные f [если только не разрешено использование подписи в случае функции])
или
запретить использование операторов функций полностью и принудительную переадресацию
или
имеет фактическое переопределение во всех случаях
или
разрешить некоторую синтаксическую декларацию для функций (по сути, использование функций), таких как:
void f(*signature*) = C2::f;
Что именно мне здесь не хватает?
Может кто-нибудь придумать сценарий, который проясняет «почему» такого выбора в стандарте?