 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Christopher Dearlove Guest
|
Posted: Fri Apr 29, 2005 8:23 am Post subject: Virtual inheritance rationale |
|
|
If I have the following class derivations
class A
class B : public virtual A
class C : public virtual B
class D : public B, C
class E : public D
then A is a virtual base of E and (assuming A doesn't have an
appropriate default constructor) E's constructor must construct
the common A base object, as well as constructing the object of
type D.
The question is why? Not why as answered by "that's what the language
definition says" but why as in "what's the rationale"? Superficially
an object of type E could just contain an object of type D as
usual and not need to know about the ugly mess within D. Of course
the answer could be just that the current rule is easy, but that
would surprise me. I'm expecting the answer is down to either a
layout issue I can't see, or a principle of least surprise in
some other hierarchy, or change to hierarchy, I can't see. (The
argument that this rule avoids further changes in D impacting on
E doesn't hold, if D were changed to virtual inheritance from
B and/or C, E would have to change.)
And of course I'm not in the remotest suggesting a change, even
if there were no good reason, it would be too late. And I expect
there is a reason, I just want to know why. (Mainly for curiosity,
but understanding why never hurts.)
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Antoun Kanawati Guest
|
Posted: Sat Apr 30, 2005 11:27 am Post subject: Re: Virtual inheritance rationale |
|
|
Christopher Dearlove wrote:
| Quote: | If I have the following class derivations
class A
class B : public virtual A
class C : public virtual B
class D : public B, C
class E : public D
then A is a virtual base of E and (assuming A doesn't have an
appropriate default constructor) E's constructor must construct
the common A base object, as well as constructing the object of
type D.
|
Non-virtual inheritance means 'contains' and 'isa' at the same time.
With single inheritance, this is not an issue, since the 'isa' implies
one containment. With multiple inheritance, having multiple instances
of the base means that they could have potentially inconsistent states.
Take a simple diamond graph:
class A { ofstream out; public: void emit(char c); }
class B : public A { public: emit(string s); }
class C : public A { public: emit(int i); }
class D : public B, public C { }
We want D to provide a single interface for emitting all three types
of data. Without virtual inheritance, we have two ofstreams that
we need to point to the same file (a problem, not a solution).
With virtual inheritance, we can easily acheive the goal by having
only one A in the object.
As a consequence of the above, we need to make sure that A gets built
only once. Hence, the most derived class is stuck with that problem.
Regardless of how you partition the problem, the construction of the
shared resource lands in the lap of the entity providing the aggregated
interface.
| Quote: | The question is why? Not why as answered by "that's what the language
definition says" but why as in "what's the rationale"? Superficially
an object of type E could just contain an object of type D as
usual and not need to know about the ugly mess within D. Of course
the answer could be just that the current rule is easy, but that
would surprise me. I'm expecting the answer is down to either a
layout issue I can't see, or a principle of least surprise in
some other hierarchy, or change to hierarchy, I can't see. (The
argument that this rule avoids further changes in D impacting on
E doesn't hold, if D were changed to virtual inheritance from
B and/or C, E would have to change.)
|
Virtual inheritance is not the same as plain inheritance; if it were,
the distinction would not exist.
To assess the benefits of virtual inheritance, you need to compare
solutions to the above problem with and without virtual inheritance.
--
A. Kanawati
[email]NO.antounk.SPAM (AT) comcast (DOT) net[/email]
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Ben Hutchings Guest
|
Posted: Sat Apr 30, 2005 11:33 am Post subject: Re: Virtual inheritance rationale |
|
|
Christopher Dearlove wrote:
| Quote: | If I have the following class derivations
class A
class B : public virtual A
class C : public virtual B
|
Presumably C is meant to derive from A, not B?
| Quote: | class D : public B, C
class E : public D
then A is a virtual base of E and (assuming A doesn't have an
appropriate default constructor) E's constructor must construct
the common A base object, as well as constructing the object of
type D.
The question is why? Not why as answered by "that's what the language
definition says" but why as in "what's the rationale"? Superficially
an object of type E could just contain an object of type D as
usual and not need to know about the ugly mess within D.
|
And in this class, would its constructors be responsible for calling
A's constructors?
class F : public virtual A, public D { ... };
What general rule are you suggesting?
| Quote: | Of course
the answer could be just that the current rule is easy, but that
would surprise me. I'm expecting the answer is down to either a
layout issue I can't see, or a principle of least surprise in
some other hierarchy, or change to hierarchy, I can't see.
snip |
Clearly a single class's constructor must be responsible for calling
each virtual base constructor, for any class that is instantiated.
This seems to be the most obvious way of selecting that class.
If you're concerned about hiding the "ugly mess", you can give A a
sensible default constructor and let D's constructor complete
initialisation by calling other member functions of A.
--
Ben Hutchings
Having problems with C++ templates? Your questions may be answered by
<http://womble.decadentplace.org.uk/c++/template-faq.html>.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Francis Glassborow Guest
|
Posted: Sat Apr 30, 2005 3:54 pm Post subject: Re: Virtual inheritance rationale |
|
|
In article <e3224f76.0504280541.4b668422 (AT) posting (DOT) google.com>,
Christopher Dearlove <chris.dearlove (AT) baesystems (DOT) com> writes
| Quote: | If I have the following class derivations
class A
class B : public virtual A
class C : public virtual B
class D : public B, C
class E : public D
then A is a virtual base of E and (assuming A doesn't have an
appropriate default constructor) E's constructor must construct
the common A base object, as well as constructing the object of
type D.
The question is why?
|
Because the view was taken that only the context of the complete object
provides the correct knowledge for instantiating a virtual base if it is
necessary to do so explicitly.
Most often virtual bases use default constructors and so the differences
are academic, but where this is not the case the designers of the
language considered that, in general, only the most derived object would
always know how its virtual bases should be constructed.
--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Christopher Dearlove Guest
|
Posted: Tue May 03, 2005 11:04 am Post subject: Re: Virtual inheritance rationale |
|
|
Ben Hutchings <ben-public-nospam (AT) decadentplace (DOT) org.uk> wrote
| Quote: | Christopher Dearlove wrote:
If I have the following class derivations
class A
class B : public virtual A
class C : public virtual B
Presumably C is meant to derive from A, not B?
|
Yes. Thanks for the catch.
| Quote: | class D : public B, C
class E : public D
then A is a virtual base of E and (assuming A doesn't have an
appropriate default constructor) E's constructor must construct
the common A base object, as well as constructing the object of
type D.
The question is why? Not why as answered by "that's what the language
definition says" but why as in "what's the rationale"? Superficially
an object of type E could just contain an object of type D as
usual and not need to know about the ugly mess within D.
And in this class, would its constructors be responsible for calling
A's constructors?
class F : public virtual A, public D { ... };
What general rule are you suggesting?
|
I'm not, except as a hypothetical, and I certainly haven't worked out an
alternative rule (and maybe it's not possible) but in my case we could
allow E to not construct A, which delegates the job of constructing A to
the next most derived object, provided this is unambiguous, namely D. (Of
course in this hypothetical alternative case, E could also choose to
construct A itself - or does this cause a problem?) In your example F would
have to do the job of constructing A, because no other class could do it
unambiguously.
| Quote: | Clearly a single class's constructor must be responsible for calling
each virtual base constructor, for any class that is instantiated.
This seems to be the most obvious way of selecting that class.
|
Agreed it's the most obvious, and no doubt easiest to define and implement.
I just wondered if there was a deeper reason, and from replies so far there
doesn't seem to be. Of course "easier and appropriate in 99% of cases" is
a good reason, but not a deep reason.
| Quote: | If you're concerned about hiding the "ugly mess", you can give A a
sensible default constructor and let D's constructor complete
initialisation by calling other member functions of A.
|
Unfortunately not possible in my case, for factory related reasons.
It's no big deal. It's just that in this case something better is, at
least superficially, possible. I wondered if I'd missed an obvious
reason why this couldn't work, or had horrible consequences.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Francis Glassborow Guest
|
Posted: Tue May 03, 2005 1:54 pm Post subject: Re: Virtual inheritance rationale |
|
|
In article <e3224f76.0505030125.7b1595eb (AT) posting (DOT) google.com>,
Christopher Dearlove <chris.dearlove (AT) baesystems (DOT) com> writes
| Quote: | It's no big deal. It's just that in this case something better is, at
least superficially, possible. I wondered if I'd missed an obvious
reason why this couldn't work, or had horrible consequences.
|
Actually, unless you have ever tried to specify the correct behaviour
for object lattices that include virtual bases with virtual functions
you have no idea how hard it was to get even a simple spec such as the
one we have to be correct. And when we try to get compiler generated
copy assignment correct in such cases it is orders of magnitude harder.
Your suggestion may seem relatively simple, but take it from one who was
involved in writing this part of the Standard, we already have too many
complications to even consider making it worse. The simple rule 'the
most derived class instantiates the virtual bases left to right depth
first' is already tough enough.
--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
James Kanze Guest
|
Posted: Thu May 05, 2005 12:05 am Post subject: Re: Virtual inheritance rationale |
|
|
Christopher Dearlove wrote:
| Quote: | Ben Hutchings <ben-public-nospam (AT) decadentplace (DOT) org.uk> wrote
in message
news:<slrnd74s2c.mfn.ben-public-nospam (AT) decadentplace (DOT) org.uk>...
Christopher Dearlove wrote:
If I have the following class derivations
class A
class B : public virtual A
class C : public virtual B
Presumably C is meant to derive from A, not B?
Yes. Thanks for the catch.
class D : public B, C
class E : public D
then A is a virtual base of E and (assuming A doesn't have an
appropriate default constructor) E's constructor must
construct the common A base object, as well as constructing
the object of type D.
The question is why? Not why as answered by "that's what the
language definition says" but why as in "what's the
rationale"? Superficially an object of type E could just
contain an object of type D as usual and not need to know
about the ugly mess within D.
And in this class, would its constructors be responsible for
calling A's constructors?
class F : public virtual A, public D { ... };
What general rule are you suggesting?
I'm not, except as a hypothetical, and I certainly haven't
worked out an alternative rule (and maybe it's not possible)
but in my case we could allow E to not construct A, which
delegates the job of constructing A to the next most derived
object, provided this is unambiguous, namely D. (Of course in
this hypothetical alternative case, E could also choose to
construct A itself - or does this cause a problem?) In your
example F would have to do the job of constructing A, because
no other class could do it unambiguously.
|
FWIW, many years ago (10 or more), John Skaller, Paul Lucas and
myself exchanged email on the subject in view of an alternative
rule, which in the original example would have had D calling the
constructor, and F in the second example. The basic rule was
that the lowest class which "caps" all of the declarations of
the virtual inheritance in the hierarchy is responsible for
calling the constructor.
The situation becomes somewhat more complicated when more than
one virtual base is involved; the class which calls the
constructor for the virtual base may not be the same for all
virtual bases.
We'd gotten far enough that I'd even worked out a conceptual
implementation; how it could be done. But we finally decided
that the resulting rule was just too complicated. And
confusing; if D, above, has two virtual bases, it may end up "in
charge" for one, and not for the other. We also realized that
for most useful applications of virtual base classes, if a class
is suitable for use as a virtual base, it is relatively easy to
provide it with a default constructor.
So we let the proposal dropt.
| Quote: | Clearly a single class's constructor must be responsible for
calling each virtual base constructor, for any class that is
instantiated. This seems to be the most obvious way of
selecting that class.
Agreed it's the most obvious,
|
Not until you start considering cases like F, above, or cases
where there are several virtual bases. Given your D and E, it's
obvious that D should be the one to call the virtual
constructors:-).
The problem here is that what is obvious in this simple case
doesn't scale to more complicated cases.
| Quote: | and no doubt easiest to define and implement. I just wondered
if there was a deeper reason, and from replies so far there
doesn't seem to be. Of course "easier and appropriate in 99%
of cases" is a good reason, but not a deep reason.
|
C++ is a pratical language. The goal is to get the job done,
not to be theoretically perfect (and perfectly unusable).
--
James Kanze mailto: [email]james.kanze (AT) free (DOT) fr[/email]
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
chris.dearlove@baesystems Guest
|
Posted: Thu May 05, 2005 12:46 pm Post subject: Re: Virtual inheritance rationale |
|
|
James Kanze wrote:
| Quote: | Christopher Dearlove wrote:
Ben Hutchings <ben-public-nospam (AT) decadentplace (DOT) org.uk> wrote:
Clearly a single class's constructor must be responsible for
calling each virtual base constructor, for any class that is
instantiated. This seems to be the most obvious way of
selecting that class.
Agreed it's the most obvious,
Not until you start considering cases like F, above, or cases
where there are several virtual bases. Given your D and E, it's
obvious that D should be the one to call the virtual
constructors:-).
The problem here is that what is obvious in this simple case
doesn't scale to more complicated cases.
|
Thanks for the illuminating reply. Just for the record however it's
the current solution that was meant to be referred to as obvious here,
not the hypothetical alternative. But split across two writers that
may itself not have been obvious.
| Quote: | I just wondered
if there was a deeper reason, and from replies so far there
doesn't seem to be. Of course "easier and appropriate in 99%
of cases" is a good reason, but not a deep reason.
C++ is a pratical language. The goal is to get the job done,
not to be theoretically perfect (and perfectly unusable).
|
No arguments there, and I meant to convey something similar.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|