«Недоступная прямая база», вызванная множественным наследованием - PullRequest
18 голосов
/ 07 ноября 2010

Оповещение о спойлере: возможно, глупый вопрос.:)

#include <iostream>

using namespace std;

class Base
{
    public:
        virtual void YourMethod(int) const = 0;
};

class Intermediate : private Base
{
    public:
        virtual void YourMethod(int i) const
        {
            cout << "Calling from Intermediate" << i << "\n";
        }
};

class Derived : private Intermediate, public Base
{
    public:
        void YourMethod(int i) const
        {
            cout << "Calling from Derived : " << i << "\n";
        }
};

int main()
{
}

Может кто-то Объясните , почему это выдает компилятор предупреждение :

main.cpp:21: warning: direct base ‘Base’ inaccessible in ‘Derived’ due to ambiguity

Теперь я понимаю, чтони в коем случае этот код не будет работать.Я хочу знать почему.Base является приватным для Intermediate, поэтому он не должен быть виден для Derived через Intermediate.Так откуда же возникает двусмысленность?В конструкторе?

Ответы [ 2 ]

26 голосов
/ 07 ноября 2010

Это не имеет ничего общего с переопределением функций. Это связано с конверсиями. Это на самом деле не имеет никакого отношения к доступности (то есть «частной» или тому подобное) напрямую. Вот более простой пример

struct A { int a; };
struct B : A { };
struct C : B, A { }; // direct A can't be referred to!

Вы можете обратиться к косвенному A объекту, сначала преобразовав в B, а затем в A:

B *b = &somec;
A *a = b;

Вы не можете сделать это с прямым объектом. Если вы попытаетесь напрямую конвертировать в A, у него будет две возможности. Отсюда следует, что невозможно сослаться на нестатические элементы данных прямого объекта A, заданного объектом Derived.

Обратите внимание, что доступность ортогональна видимости. Что-то может быть доступно, даже если оно не видно (например, ссылаясь на него с помощью квалифицированного имени), а что-то может быть видимым, даже если оно недоступно. Даже если все перечисленные выше производные будут объявлены private, проблема все равно будет обнаружена: доступ проверяется последним - это не повлияет на поиск имени или правила преобразования.

Кроме того, любой может привести к однозначному закрытому базовому классу с определенным поведением (исключение составляет стандарт C ++), используя приведение в стиле C, даже если обычно для этого не предоставляется доступ. А потом еще есть друзья и сам класс, который может свободно конвертировать.

7 голосов
/ 07 ноября 2010

Ответ Йоханнеса охватывает основные факты. Но есть еще кое-что. Итак, рассмотрим

struct Base
{
    Base( int ) {}
    void foo() const {}
};

struct Intermediate: Base
{
    Intermediate( int x )
        : Base( x )
    {}
};

struct Derived: Intermediate, Base
{
    Derived( int x )
        : Intermediate( x )
        , Base( x )         // OK
    {}
};

int main()
{
    Derived o( 667 );
    o.foo();                // !Oops, ambiguous.
    o.Base::foo();          // !Oops, still ambiguous.
}

Когда я собираю, я получаю, как сейчас (после ответа Йоханнеса), вы ожидаете,

C:\test> gnuc x.cpp
x.cpp:15: warning: direct base 'Base' inaccessible in 'Derived' due to ambiguity
x.cpp: In function 'int main()':
x.cpp:25: error: request for member 'foo' is ambiguous
x.cpp:4: error: candidates are: void Base::foo() const
x.cpp:4: error:                 void Base::foo() const
x.cpp:26: error: 'Base' is an ambiguous base of 'Derived'

C:\test> msvc x.cpp
x.cpp
x.cpp(15) : warning C4584: 'Derived' : base-class 'Base' is already a base-class of 'Intermediate'
        x.cpp(2) : see declaration of 'Base'
        x.cpp(7) : see declaration of 'Intermediate'
x.cpp(25) : error C2385: ambiguous access of 'foo'
        could be the 'foo' in base 'Base'
        or could be the 'foo' in base 'Base'
x.cpp(25) : error C3861: 'foo': identifier not found

C:\test> _

Способ решения зависит от того, все ли в порядке с одним подобъектом класса Base (как в случае, когда Base - чистый интерфейс), или Intermediate действительно требует своего собственного Base sub -объект.

Последний случай, два Base подобъекта, вероятно, не то, что вы хотите, но если вы хотите этого, тогда одно из лекарств - ввести еще один промежуточный класс, скажем, ResolvableBase.

Как:

struct Base
{
    Base( int ) {}
    void foo() const {}
};

struct Intermediate: Base
{
    Intermediate( int x )
        : Base( x )
    {}
};

struct ResolvableBase: Base
{
    ResolvableBase( int x ): Base( x ) {}
};

struct Derived: Intermediate, ResolvableBase
{
    Derived( int x )
        : Intermediate( x )
        , ResolvableBase( x )
    {}
};

int main()
{
    Derived o( 667 );
    o.ResolvableBase::foo();    // OK.
}

В первом случае, например, где Base является интерфейсом и необходим только один субобъект Base, вы можете использовать виртуальное наследование.

Виртуальное наследование обычно добавляет некоторые накладные расходы времени выполнения, и Visual C ++ не слишком любит его.

Но это позволяет вам «наследовать» реализацию интерфейса, как в Java и C #:

struct Base
{
    Base( int ) {}
    virtual void foo() const = 0;
};

struct Intermediate: virtual Base
{
    Intermediate( int x )
        : Base( x )
    {}
    void foo() const {}     // An implementation of Base::foo
};

struct Derived: virtual Base, Intermediate
{
    Derived( int x )
        : Base( x )
        , Intermediate( x )
    {}
};

int main()
{
    Derived o( 667 );
    o.foo();    // OK.
}

Тонкость: я изменил порядок списка наследования, чтобы избежать глупых предупреждений g ++ о порядке инициализации.

Раздражение: Visual C ++ выдает sillywarning C4250 о наследовании (реализации) через доминирование. Это как «предупреждение: вы используете стандартную основную функцию». Ну что ж, просто выключи его.

Приветствия & hth.,

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