Topic: Why must non-leaf classes explicitly call the constructor of a virtual base class?


Author: Pierre Baillargeon <pb@artquest.net>
Date: 2000/11/10
Raw View
James Kuyper wrote:
>
> Pierre Baillargeon wrote:
> >
> >
> > If D1 derives from two classes that have incompatible requirements, the
> > fault lies in the designer. Virtual bases have no bearing on improper
> > designs.
>
> You're perfect correct, IF the two classes have incompatible
> requirements. However, the case that's being considered here is one
> where the requirements may be compatible, but not necessarily
> equivalent. For instance (appropriate class definitions implied):
>
>         enum vtype{TYPE_A=1, TYPE_B=2};
>         VB::VB(enum vtype new_type): this_type(new_type) {};
>         L::L(): VB(TYPE_A) {};
>         R::R(): VB(TYPE_B) {};
>         D1::D1(): VB(TYPE_A | TYPE_B) {};
>

But then the rule, which would say taht the first "most-leaf" class that
initializes the virtual base does the initialization, works. If a D2
derives from D1, why should it have to restate that it is a TYPE_A |
TYPE_B ? Especially if D1 and VB are part of a library and VB is
supposed not to be known to user of the library (D2).

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]






Author: James.Kanze@dresdner-bank.com
Date: 2000/11/07
Raw View
In article <UcrQ75AUDv$5Ew2G@ntlworld.com>,
  Francis Glassborow <francisG@robinton.demon.co.uk> wrote:
> In article <s38mt8.b62.ln@tinus.intra.doosys.com>, Wil Evers
> <bouncer@dev.null> writes

> >If that's the committee's official position, then the use of
> >virtual base classes lacking a default constructor should be
> >deprecated in the next version of the standard.

> The Committees AFAIK do not have an official position, however they
> are unlikely to spend time discussing an issue like this one unless
> someone present takes the trouble to present a paper on the subject
> and can provide all relevant wording to deal with this special case
> and demonstrate that it is free of affects to anything else. It is a
> matter of priorities and committee time is a precious resource.

For what it's worth, Paul Lucas, John Skaller and myself considered
the problem in detail some years back.  We actually started to develop
a proposal to eliminate the problem, but in the end, we dropped it
because it became clear to us that virtual base classes without a
default constructor were simply bad practice anyway.

The reason behind the rule is simple: if the most derived class
doesn't initialize the virtual base, who should.  Consider the
following hierarchy:

        VB
       /  \
      L    R
       \  /
        D1
        |
        D2

Ignoring the virtual base, the order of initialization would be L, R,
D1 and D2.  Now, it is clear that VB must be initialized before L.
Suppose thus that L initializes it.  How can L possibly know the
correct arguments for it to serve equally well as a base to R.  At the
earliest, it can be initialized by D1, who "knows" that it serves as
base for both L and R, and can choose an appropriate argument.

It would thus seem that the correct solution is for it to be
initialized by D1.  However, consider the following:

        VB   VB2
       /  \ / |
      L    R  |
       \  /   |
        D1    |
        |     |
        D2    |
         \   /
          \ /
           D3

Of course, the correct solution in this case would be for VB to be
initialized by D1, but VB2 to be initailized by D3.

Now consider that all of the classes are separately compiled.  And
that later in the game, someone adds a D4 which derives from D3, and
virtually from VB.  Suddenly, VB must be initialized from D4, and
*not* from D1.

How do you implement this?  In the usual implementation, the compiler
generates a hidden boolean argument, which is set true for the calls
of the base class constructors from the derived class constructor, and
false everywhere else.  This is relatively simple.  The alternative,
if one accepts that the initialization must be at least high enough in
the hierarchy so that all users are known, is to pass one flag per
virtual base.  Doable of course, but is it worth it?

And of course, the reason why it is generally considered bad practice
to require a parameter of a virtual base: suppose that VB has an int
parameter.  What happens if L needs it to be 0, and R to be 1?

--
James Kanze                               mailto:kanze@gabi-soft.de
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
Ziegelh   ttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627


Sent via Deja.com http://www.deja.com/
Before you buy.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]






Author: Pierre Baillargeon <pb@artquest.net>
Date: 2000/11/08
Raw View
James.Kanze@dresdner-bank.com wrote:
>
> The reason behind the rule is simple: if the most derived class
> doesn't initialize the virtual base, who should.  Consider the
> following hierarchy:
>
>         VB
>        /  \
>       L    R
>        \  /
>         D1
>         |
>         D2
>
> And of course, the reason why it is generally considered bad practice
> to require a parameter of a virtual base: suppose that VB has an int
> parameter.  What happens if L needs it to be 0, and R to be 1?

If D1 derives from two classes that have incompatible requirements, the
fault lies in the designer. Virtual bases have no bearing on improper
designs.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]






Author: James Kuyper <kuyper@wizard.net>
Date: 2000/11/09
Raw View
Pierre Baillargeon wrote:
>
> James.Kanze@dresdner-bank.com wrote:
> >
> > The reason behind the rule is simple: if the most derived class
> > doesn't initialize the virtual base, who should.  Consider the
> > following hierarchy:
> >
> >         VB
> >        /  \
> >       L    R
> >        \  /
> >         D1
> >         |
> >         D2
> >
> > And of course, the reason why it is generally considered bad practice
> > to require a parameter of a virtual base: suppose that VB has an int
> > parameter.  What happens if L needs it to be 0, and R to be 1?
>
> If D1 derives from two classes that have incompatible requirements, the
> fault lies in the designer. Virtual bases have no bearing on improper
> designs.

You're perfect correct, IF the two classes have incompatible
requirements. However, the case that's being considered here is one
where the requirements may be compatible, but not necessarily
equivalent. For instance (appropriate class definitions implied):

 enum vtype{TYPE_A=1, TYPE_B=2};
 VB::VB(enum vtype new_type): this_type(new_type) {};
 L::L(): VB(TYPE_A) {};
 R::R(): VB(TYPE_B) {};
 D1::D1(): VB(TYPE_A | TYPE_B) {};

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]






Author: "Valentin Bonnard" <Valentin.Bonnard@free.fr>
Date: Tue, 31 Oct 2000 16:47:26 GMT
Raw View
Wil Evers wrote:

> In article <bzfjwwCGug$5EwkJ@ntlworld.com>, Francis Glassborow wrote:
>
>> In article <8thah6$sl5$1@news.inet.tele.dk>, Per Velschow
>> <per@velschow.com> writes
>>
>> >Consider this general class hierarchy:
>> >
>> >class A {
>> >public:
>> >  A(int i) : i(i) {}
>> >private:
>> >  const int i;
>> >};
>> >
>> >class B : public virtual A {
>> >public:
>> >  B() : A(42) {}
>> >  virtual void foo() = 0;
>> >};
>> >
>> >class C : public B {
>> >public:
>> >  C(int i) : A(i) {}
>> >};
>> >
>> > Now, I understand why it is necessary for the constructor of
>> > class C to call the constructor of class A. But why should I be
>> > forced to have the constructor of class B call it also. As I
>> > understand it, this call cannot ever be executed since B is
>> > abstract.
>>
>> [snip]
>>
>> We do not normally consider support for what is generally
>> considered to be poor programming practice. Many, if not all,

Depends who are the experts...

I am an expert in the ISO sens, and I consider that it is
good practice and advise people to use non default constructors
in virtual bases.

I do it myself.

>> experts advise against virtual base classes that lack a default
>> ctor, and if you had one you would not have a problem.
>
> If that's the committee's official position,

No, it isn't. There is no such thing as an official position
-- not even a  committee position. Each committee member has
its own ideas about C++, how it should be used, what is Good
Practice and what is not.

> then the use of virtual
> base classes lacking a default constructor should be deprecated in the
> next version of the standard.

That wouldn't follow.

> According to my crystal ball, that won't happen.  Here's why:
>
> (1) It is easy to find examples showing that a non-default constructor
> in a virtual base class can actually do something useful.  While
> there may be alternatives, these aren't exactly easy to do either.

Well, a virtual base class with data members and ctors that takes
arguments just makes a lot of sens. I don't know why people tend to
advise against using them, perhaps the old fear of MI (of diamonds
in particular).

Needless to say, MI is one my friends, and, even if I am not a
woman,  I don't dislike diamonds.

> If my crystal ball is right, then Per's question stands.  Why would
> the compiler require the user to supply information that is known to
> be ignored?

Because it follows a stupid standard   ?

--
Valentin Bonnard

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]





Author: Francis Glassborow <francis.glassborow@ntlworld.com>
Date: Tue, 31 Oct 2000 18:19:10 GMT
Raw View
In article <s38mt8.b62.ln@tinus.intra.doosys.com>, Wil Evers
<bouncer@dev.null> writes
>If that's the committee's official position, then the use of virtual
>base classes lacking a default constructor should be deprecated in the
>next version of the standard.

The Committees AFAIK do not have an official position, however they are
unlikely to spend time discussing an issue like this one unless someone
present takes the trouble to present a paper on the subject and can
provide all relevant wording to deal with this special case and
demonstrate that it is free of affects to anything else. It is a matter
of priorities and committee time is a precious resource.

Francis Glassborow      Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA          +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]





Author: Wil Evers <bouncer@dev.null>
Date: Wed, 1 Nov 2000 15:48:39 GMT
Raw View
In article <8tmo8e$270j$1@nef.ens.fr>, Valentin Bonnard wrote:

> Wil Evers wrote:
>=20
> > In article <8thah6$sl5$1@news.inet.tele.dk>, Per Velschow
> > <per@velschow.com> writes
> >=20
> > >Consider this general class hierarchy:
> > >
> > >class A {
> > >public:
> > >  A(int i) : i(i) {}
> > >private:
> > >  const int i;
> > >};
> > >
> > >class B : public virtual A {
> > >public:
> > >  B() : A(42) {}
> > >  virtual void foo() =3D 0;
> > >};
> > >
> > >class C : public B {
> > >public:
> > >  C(int i) : A(i) {}
> > >};

[snip]

> > Why would the compiler require the user to supply information
> > that is known to be ignored?
>=20
> Because it follows a stupid standard=A0?

After re-reading the standard, I'm not so sure how stupid it really
is.  Here's the relevant quote, taken from section 12.6.2
[class.base.init] verse 6:

   All sub-objects representing virtual base classes are initialized
   by the constructor ***of the most derived class*** (1.8). If the
   constructor ***of the most derived class*** does not specify a
   mem-initializer for a virtual base class V, then V's default
   constructor is called to initialize the virtual base class
   subobject. If V does not have an accessible default constructor,
   the initialization is ill-formed.

Since B is abstract, it can never be the most derived class, so
I'm tempted to say that the standard does *not* require B's
constructor to specify a mem-initializer for A.

But what if B was not abstract, and defined as follows?

class B : public virtual A {
public:
  B() {}
  virtual void foo();
};

Here's another quote, taken from section 1.8 [intro.object], verse 4:

  If a complete object, a data member (9.2), or an array element is
  of class type, its type is considered the most derived class

This clearly indicates that whether B is considered to be the most
derived class depends on its use. Should we conclude that the
standard does not even require a non-abstract version of B to
specify a mem-initializer for A, as long as it is not used as the
most derived class?

Strange as this may seem at first, I can imagine how such a
constraint ("don't use B as a most derived class"), could be
implemented.  After all, if B's constructor is defined as

  B::B() : A(42) {}

the compiler effectively generates two versions of B's constructor:

(1) a version that calls A's constructor, used when B is used as the
most derived class.=20
(2) a version that does not call A's constructor, used when B is
used as a base class.

By simply not supplying an entry point for (1), a compiler could
enforce that even a non-abstract version of B will never be used as
a most derived class. =20

- Wil

--=20
Wil Evers, DOOSYS IT Consultants, Maarssen, Holland
[Wil underscore Evers at doosys dot com]

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]





Author: Francis Glassborow <francis.glassborow@ntlworld.com>
Date: Tue, 31 Oct 2000 00:03:16 GMT
Raw View
In article <8thah6$sl5$1@news.inet.tele.dk>, Per Velschow
<per@velschow.com> writes
>Consider this general class hierarchy:
>
>class A {
>public:
>  A(int i) : i(i) {}
>private:
>  const int i;
>};
>
>class B : public virtual A {
>public:
>  B() : A(42) {}
>  virtual void foo() = 0;
>};
>
>class C : public B {
>public:
>  C(int i) : A(i) {}
>};
>
>Now, I understand why it is necessary for the constructor of class C to call
>the constructor of class A. But why should I be forced to have the
>constructor of class B call it also. As I understand it, this call cannot
>ever be executed since B is abstract.
>
>It seems kind of silly that I have to make such a "dummy" constructor call
>in class B. It would be much better if the compiler just considered B an
>abstract class (even without the pure virtual function).
>
>Any thought on this? Could this be considered a revision to the Standard?

We do not normally consider support for what is generally considered to
be poor programming practice. Many, if not all, experts advise against
virtual base classes that lack a default ctor, and if you had one you
would not have a problem.


Francis Glassborow      Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA          +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]





Author: Wil Evers <bouncer@dev.null>
Date: Tue, 31 Oct 2000 14:29:59 GMT
Raw View
In article <bzfjwwCGug$5EwkJ@ntlworld.com>, Francis Glassborow wrote:

> In article <8thah6$sl5$1@news.inet.tele.dk>, Per Velschow
> <per@velschow.com> writes
>
> >Consider this general class hierarchy:
> >
> >class A {
> >public:
> >  A(int i) : i(i) {}
> >private:
> >  const int i;
> >};
> >
> >class B : public virtual A {
> >public:
> >  B() : A(42) {}
> >  virtual void foo() = 0;
> >};
> >
> >class C : public B {
> >public:
> >  C(int i) : A(i) {}
> >};
> >
> > Now, I understand why it is necessary for the constructor of
> > class C to call the constructor of class A. But why should I be
> > forced to have the constructor of class B call it also. As I
> > understand it, this call cannot ever be executed since B is
> > abstract.
>
> [snip]
>
> We do not normally consider support for what is generally
> considered to be poor programming practice. Many, if not all,
> experts advise against virtual base classes that lack a default
> ctor, and if you had one you would not have a problem.

If that's the committee's official position, then the use of virtual
base classes lacking a default constructor should be deprecated in the
next version of the standard.

According to my crystal ball, that won't happen.  Here's why:

(1) It is easy to find examples showing that a non-default constructor
in a virtual base class can actually do something useful.  While
there may be alternatives, these aren't exactly easy to do either.
(2) A virtual base class with *both* a default- and a non-default
constructor is a sure recipe for trouble.
(3) Most people hate trouble.

If my crystal ball is right, then Per's question stands.  Why would
the compiler require the user to supply information that is known to
be ignored?

- Wil

--
Wil Evers, DOOSYS IT Consultants, Maarssen, Holland
[Wil underscore Evers at doosys dot com]

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]





Author: "Per Velschow" <per@velschow.com>
Date: Mon, 30 Oct 2000 20:05:35 GMT
Raw View
Consider this general class hierarchy:

class A {
public:
  A(int i) : i(i) {}
private:
  const int i;
};

class B : public virtual A {
public:
  B() : A(42) {}
  virtual void foo() = 0;
};

class C : public B {
public:
  C(int i) : A(i) {}
};

Now, I understand why it is necessary for the constructor of class C to call
the constructor of class A. But why should I be forced to have the
constructor of class B call it also. As I understand it, this call cannot
ever be executed since B is abstract.

It seems kind of silly that I have to make such a "dummy" constructor call
in class B. It would be much better if the compiler just considered B an
abstract class (even without the pure virtual function).

Any thought on this? Could this be considered a revision to the Standard?

/Per



---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]
[ Note that the FAQ URL has changed!  Please update your bookmarks.     ]