Ответ Йоханнеса охватывает основные факты. Но есть еще кое-что. Итак, рассмотрим
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.,