AFAIK virtual inheritance exist exclusively as a solution to diamond
inheritance. I'm not aware of any other uses.
What's the "diamond problem"? Virtual inheritance exists to
allow multiple inheritance of interfaces, where some of the
interfaces may themselves extend other interfaces. Or to
support the mixin idiom, where different implementation classes
are responsible for different aspects of the same interface.
(Arguably, virtual inheritance should be the default, since it's
generally what you want when it makes a difference.)
--
James Kanze
Wikipedia is your friend.
> Wikipedia is your friend.
> http://en.wikipedia.org/wiki/Diamond_problem
OK. The article isn't written all that clearly, but if I
understand the first section correctly, the problem would only
occur in C++ if you used virtual inheritance; virtual
inheritance creates the diamond problem, rather than solves it.
Of course, the next section, "Approaches", seems to be
dicussing something else; it certainly doesn't discuss how C++
handles the problem described immediately above. (Name hiding,
and a compiler error if this does not resolve the problem.)
In other words, the author of the article doesn't seem to know
what the "diamond problem" is himself, since he discusses two
different things under the vocable.
--
James Kanze
No, virtual inheritance is a solution to the diamond problem.
The diamond problem happens with multiple inheritance when the
multiple base classes have a common base class of their own. The problem
is: Should this common base class be duplicated in the
multiple-inherited class, or should it be merged into one?
If, for example, class A has a member function named foo(), class B
inherits from A, class C inherits from A, and finally class D inherits
from both B and C, and then you create an object of type D and call its
foo() member function, what should happen?
Moreover, if a function in B calls a function in A which modifies some
member variable of A, and some other function in C calls that same
function in A, what should happen? Should both the B and C calls cause
the *same* member variable to be modified, or should these two members
be separate (so that B has its own state, separate from C which has its
own)?
Suppose you have an object of type D. If some function somewhere
expects an object of type A, and a member function of B gives 'this' to
it, what should happen? Likewise if a member function of C calls that
function taking an object of type A. Should they pass their common A
sub-object, or should they pass separate A sub-objects? (This can make a
huge difference not only on the state of the A part of the object, but
because A might have some virtual function which both B and C implement.)
Not using virtual inheritance is taking one stance (B and C should be
separate and have no common base), while using virtual inheritance is
taking another (their bases should be merged). Thus virtual inheritance
offers one solution to the problem.
> Of course, the next section, "Approaches", seems to be
> dicussing something else; it certainly doesn't discuss how C++
> handles the problem described immediately above.
It discusses how some languages supporting multiple inheritance deal
with the problems.
> In other words, the author of the article doesn't seem to know
> what the "diamond problem" is himself, since he discusses two
> different things under the vocable.
I think it's you who is not understanding what the problem is (which,
quite frankly, I find quite surprising).
> >> Wikipedia is your friend.
> >>http://en.wikipedia.org/wiki/Diamond_problem
> > OK. The article isn't written all that clearly, but if I
> > understand the first section correctly, the problem would only
> > occur in C++ if you used virtual inheritance; virtual
> > inheritance creates the diamond problem, rather than solves it.
> No, virtual inheritance is a solution to the diamond problem.
> The diamond problem happens with multiple inheritance when the
> multiple base classes have a common base class of their own.
Which only happens if there is virtual inheritance.
> The problem is: Should this common base class be duplicated in
> the multiple-inherited class, or should it be merged into one?
I think we have a problem of vocabulary. If the common base
class isn't merged, it isn't common---it's two distinct bases
(which perhaps happen to have the same type).
> If, for example, class A has a member function named foo(),
> class B inherits from A, class C inherits from A, and finally
> class D inherits from both B and C, and then you create an
> object of type D and call its foo() member function, what
> should happen?
Exactly. Now forget about A. Remove it completely from the
example: class D inherits from both B and C, both B and C
defined foo(), and you call foo in an instance of D. Nothing
diamond-like in sight, and exactly the same problem.
(Obviously, the author of the Wikipedia article doesn't really
understand what he is talking about. Happens sometimes.) And
even with A, if the problem occurs, inheriting virtually doesn't
change anything.
> Moreover, if a function in B calls a function in A which
> modifies some member variable of A, and some other function in
> C calls that same function in A, what should happen?
A shouldn't have any member variables:-). (Seriously, it is
*usually* a design error if A has any state. But of course,
like most rules, there are occasional exceptions.)
More importantly, what should happen depends on the design, but
most often, what should happen is that there is only one
instance of A. In other words, you should have a diamond. (In
which case, the diamond isn't the problem, it's the solution.)
> Should both the B and C calls cause the *same* member variable
> to be modified, or should these two members be separate (so
> that B has its own state, separate from C which has its own)?
It depends on the design. Any reasonably good OO language will
support both.
> Suppose you have an object of type D. If some function
> somewhere expects an object of type A, and a member function
> of B gives 'this' to it, what should happen? Likewise if a
> member function of C calls that function taking an object of
> type A. Should they pass their common A sub-object, or should
> they pass separate A sub-objects? (This can make a huge
> difference not only on the state of the A part of the object,
> but because A might have some virtual function which both B
> and C implement.)
What they should pass isn't really the question. The question
is whether they should have separate sub-objects. Most of the
time, at least in the case of public inheritance, they
shouldn't; sometimes they should. Whatever the case, however, C
should pass the pointer to its A sub-object, and not that of B's
sub-object. (C shouldn't even know that B exists.) If they're
common (a diamond shaped hierarchy), then the address of C's
sub-object will be the same as that of B's sub-object. If
they're different (no diamond), then of course, C should use the
address of its sub-object.
And none of this, IMHO, deserves any name with the word
"problem" in it. (But maybe I'm influenced by the fact that my
first OO language was C++, where you can choose which ever
solution the design requires.)
> Not using virtual inheritance is taking one stance (B and C
> should be separate and have no common base), while using
> virtual inheritance is taking another (their bases should be
> merged). Thus virtual inheritance offers one solution to the
> problem.
First, there's no problem; your design determines whether you
need a diamond or not. And the choice between virtual and
non-virtual inheritance allows choosing the appropriate solution
for the design.
> > Of course, the next section, "Approaches", seems to be
> > dicussing something else; it certainly doesn't discuss how
> > C++ handles the problem described immediately above.
> It discusses how some languages supporting multiple
> inheritance deal with the problems.
It doesn't say anything about the "problem" (function call
ambiguity) presented in the first section. The "Approaches"
section does present more or less the possibilities different
languages offer with regards to creating more or less complex
inheritance hierarchies. (On rereading it, he does seem to
address the ambiguity question in his discussion of some of the
other languages. But when he mentions it, he doesn't say
anything about instances.)
The duplication (or not) and the function call ambiguity are two
separate, orthogonal issues.
> > In other words, the author of the article doesn't seem to
> > know what the "diamond problem" is himself, since he
> > discusses two different things under the vocable.
> I think it's you who is not understanding what the problem is
> (which, quite frankly, I find quite surprising).
The reason I don't understand what it is is that so many people
present so many different opinions about what it is. And that
to date, no one has shown me anything which could be considered
a problem with the diamond inheritance hierarchy; the problem
could be (in some other languages) that they don't support the
diamond hierarchy---a no-diamond problem. And the article
confused me even further, because the author mixes unrelated
issues.
--
James Kanze
>> > OK. The article isn't written all that clearly, but if I
>> > understand the first section correctly, the problem would only
>> > occur in C++ if you used virtual inheritance; virtual
>> > inheritance creates the diamond problem, rather than solves it.
>
>> No, virtual inheritance is a solution to the diamond problem.
>
>> The diamond problem happens with multiple inheritance when the
>> multiple base classes have a common base class of their own.
>
> Which only happens if there is virtual inheritance.
That is the strange view.
You design things in abstract, and (say in UML) discover that you have
ostream and istream that work fine in solo, and need iostream, that merry
them. But they already have that ios as base class, that *shall* be common
in iostream. (if actually drawn the usual way looks like a rombus...).
The design with the requirements is far from any kind of implementation, so
it can hardly be created from existance of virtual inheritance. IIRC
virtual inheritance's entering C++ was massively motivated by that iostream
library design, without which it could not be implemented to that spec.
>> The problem is: Should this common base class be duplicated in
>> the multiple-inherited class, or should it be merged into one?
>
> I think we have a problem of vocabulary. If the common base
> class isn't merged, it isn't common---it's two distinct bases
> (which perhaps happen to have the same type).
The class is clearly common -- the question is whether the object is common
or not.
Just often the "bace class sub-object" is in practice abbreviated as "base
class" then it can create this very confusion.
>> Moreover, if a function in B calls a function in A which
>> modifies some member variable of A, and some other function in
>> C calls that same function in A, what should happen?
>
> A shouldn't have any member variables:-). (Seriously, it is
> *usually* a design error if A has any state.
If A is just interface there's obvoisly no state. In case of ios it very
likely has state.
In old days of C++ we had many Object - based hierarchies. With Object
having RTTI primitives, and possibly factory support. And most anything was
supposed to derive from Object or some of its descendant provided by the
framework. Brobably had some state too. Though MI was pretty hosed with
those beasts unless one discovered that deriving from object is better
dismissed ;-))
>> Should both the B and C calls cause the *same* member variable
>> to be modified, or should these two members be separate (so
>> that B has its own state, separate from C which has its own)?
>
> It depends on the design. Any reasonably good OO language will
> support both.
Ah, do we have abdundance of reasonably good OO languages?
> What they should pass isn't really the question. The question
> is whether they should have separate sub-objects. Most of the
> time, at least in the case of public inheritance, they
> shouldn't; sometimes they should.
And for private inheritance they most likely shouldn't.
> First, there's no problem; your design determines whether you
> need a diamond or not. And the choice between virtual and
> non-virtual inheritance allows choosing the appropriate solution
> for the design.
Certainly provided that your supposedly OO-supporting language allows a
solutio that looks like the model. If not, you have quite a problem don't
you? ;-)
> The reason I don't understand what it is is that so many people
> present so many different opinions about what it is. And that
> to date, no one has shown me anything which could be considered
> a problem with the diamond inheritance hierarchy; the problem
> could be (in some other languages) that they don't support the
> diamond hierarchy---a no-diamond problem.
Hm, that looks like another vocabulary issue.
You can use it to prevent further derivation from a class:
class FinalHelper
{
protected:
FinalHelper() {}
};
class MyFinal : virtual private FinalHelper
{
};
MyFinal can not be derived from: a derived class's constructor would
be responsible for constructing FinalHelper but FinalHelper is not
accessible there.
This is just a side effect of the rules for virtual base class
construction though.
You are confusing classes with objects.
If B inherits from A, and C inherits from A, then A is a common base
class to both B and C. (For example, if a function expects a reference
of type A, an object of type B or type C can be given to it.)
Diamond inheritance introduces an ambiguity: If D inherits from both B
and C, should A be instantiated only once in objects of type D, or
should it be instantiated twice, once for the B part and once for the D
part inside D? *That* is the diamond problem.
Virtual inheritance is *one solution* to that problem (not the only
possible solution, but one of them).
>> The problem is: Should this common base class be duplicated in
>> the multiple-inherited class, or should it be merged into one?
>
> I think we have a problem of vocabulary. If the common base
> class isn't merged, it isn't common---it's two distinct bases
> (which perhaps happen to have the same type).
You are indeed confusing the concepts of class and object
(instantiation of that class). A is still a base class for both B and C
regardless of whether it's instantiated once of twice inside D.
The question is whether A should appear only once inside D (and thus
shared by both B and C) or not.
>> If, for example, class A has a member function named foo(),
>> class B inherits from A, class C inherits from A, and finally
>> class D inherits from both B and C, and then you create an
>> object of type D and call its foo() member function, what
>> should happen?
>
> Exactly. Now forget about A. Remove it completely from the
> example: class D inherits from both B and C, both B and C
> defined foo(), and you call foo in an instance of D. Nothing
> diamond-like in sight, and exactly the same problem.
> (Obviously, the author of the Wikipedia article doesn't really
> understand what he is talking about. Happens sometimes.) And
> even with A, if the problem occurs, inheriting virtually doesn't
> change anything.
Knowing your vast knowledge about C++, I'm seriously surprised that
you don't know this better. If A has a foo() function and you use
virtual inheritance in a diamond inheritance situation, there's no
ambiguity anymore:
//-------------------------------------------------------------
#include <iostream>
class A
{
public:
void foo() { std::cout << "A::foo()\n"; }
};
class B: virtual public A {};
class C: virtual public A {};
class D: public B, public C {};
int main()
{
D d;
d.foo(); // Compiles ok, no ambiguity
}
//-------------------------------------------------------------
Now remove those 'virtual' keywords and you'll get a compiler error
because the "d.foo()" call is ambiguous.
The reason why there's no ambiguity with virtual inheritance is that
with it, A is instantiated only once inside the 'd' object and thus
there's only one A::foo() to be called (or technically speaking, there's
only one "this" pointer to be used when calling A::foo(), so there's no
ambiguity).
>> Moreover, if a function in B calls a function in A which
>> modifies some member variable of A, and some other function in
>> C calls that same function in A, what should happen?
>
> A shouldn't have any member variables:-). (Seriously, it is
> *usually* a design error if A has any state. But of course,
> like most rules, there are occasional exceptions.)
Base classes should not have any member variables? What are you
talking about? Where are you getting this from?
> More importantly, what should happen depends on the design, but
> most often, what should happen is that there is only one
> instance of A. In other words, you should have a diamond. (In
> which case, the diamond isn't the problem, it's the solution.)
You are not understanding what "diamond inheritance" means. It's a
diamond formed in the inheritance hierarchy.
The diamond inheritance shape is not the problem. How to instantiate
objects of the most derived type is the problem.
>> Should both the B and C calls cause the *same* member variable
>> to be modified, or should these two members be separate (so
>> that B has its own state, separate from C which has its own)?
>
> It depends on the design. Any reasonably good OO language will
> support both.
You are missing the point. I was describing why the "diamond problem"
is a problem. It was not a question I asked you.
> And none of this, IMHO, deserves any name with the word
> "problem" in it.
It does because there is an ambiguity, which is the problem. And
there's no universal solution to the problem. Instead, you have to make
a design decision about which solution you want to use, and every
solution has its own advantages and disadvantages. (Many OO languages
avert the problem completely by not supporting multiple inheritance at all.)
>> Not using virtual inheritance is taking one stance (B and C
>> should be separate and have no common base), while using
>> virtual inheritance is taking another (their bases should be
>> merged). Thus virtual inheritance offers one solution to the
>> problem.
>
> First, there's no problem; your design determines whether you
> need a diamond or not. And the choice between virtual and
> non-virtual inheritance allows choosing the appropriate solution
> for the design.
What you wrote is contradictory. If there is no problem then no
solution should be needed either. However, as you yourself admit, the
programmer has to choose a solution. A solution is needed because there
is a problem (more specifically, it's a problem of ambiguity).
> The duplication (or not) and the function call ambiguity are two
> separate, orthogonal issues.
No, they aren't, because the base class member functions operate on
the base class member variables, and thus it makes a big difference
whether the base class has been instantiated once or twice in the most
derived class.
> The reason I don't understand what it is is that so many people
> present so many different opinions about what it is.
It seems to be that you are simply refusing to acknowledge that the
ambiguity presented by diamond inheritance is a problem needing a
solution. Rather contradictorily, you acknowledge the existence of the
solutions themselves, but not the problem they were created to solve.