Где в стандарте переадресация на базовый класс, требуемый в этих ситуациях? - PullRequest
2 голосов
/ 29 мая 2010

Может быть, даже лучше: почему стандарт требует перенаправления в базовый класс в этих ситуациях? (да да да - почему? - потому что.)

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;

Что именно мне здесь не хватает? Может кто-нибудь придумать сценарий, который проясняет «почему» такого выбора в стандарте?

1 Ответ

3 голосов
/ 29 мая 2010

Стандарт C ++ говорит в §10.3 / 2:

Правила поиска членов (10.2) используются для определения окончательного переопределения для виртуальной функции в области действия производного класса, но игнорируются имена, введенные using-объявлений .

Таким образом, даже если вы используете using B2::f; для переноса B2::f() в производный класс, он не считается переопределением B1::f().

Таким образом, D2 является абстрактным из-за §10.4 / 4:

Класс является абстрактным, если он содержит или наследует хотя бы одну чисто виртуальную функцию, для которой конечный переопределитель является чисто виртуальным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...