Topic: Virtual base class initialization in constructors


Author: Marc Girod <girod@stybba.ntc.nokia.com>
Date: 1998/12/09
Raw View
>>>>> "JG" == Jeff Greif <jmg@spam-me-not.trivida.com> writes:

JG> Paragraph 12.6.2.6 seems to imply that if V is a virtual base of A with no
JG> default constructor in the hierarchy below,

JG> V <- A <- B <- C ...<- J <- K

JG> that each class B,...K must initialize V explicitly.
[...]
JG> This seems to place an unreasonable burden on the designer of
JG> higher-level classes.

Indeed, so that the conclusion is simple: never inherit virtually from
a class with data members (needed to be initialized by a non default
constructor).
This is a simple advice. Last time I saw it, it was in "Inside the C++
Object Model", by Stanley Lippman, 4.2, p 139.

Using the mixin scheme, it is not even restrictive.

Best Regards!
Marc

--
Marc Girod                Hiomo 5/1          Voice:  +358-9-511 23746
Nokia Telecommunications  P.O. Box 320       Mobile: +358-40-569 7954
NWS/NMS/NMS for Data      00045 NOKIA Group  Fax:    +358-9-511 23580
                          Finland            marc.girod@ntc.nokia.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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: sbnaran@localhost.localdomain.COM (Siemel Naran)
Date: 1998/12/09
Raw View
On 9 Dec 1998 17:28:20 GMT, Stefan Rupp <struppi@gia.rwth-aachen.de> wrote:

>But if a base class does not have any data members, why should I make
>it virtual? Does that make any sense?

One reason is to take advantage of virtual functions.  The standard
requires each virtual function in the base class to have a unique
final overrider.


//10.3  Virtual Functions  [class.virtual]

struct A { virtual void f(); };
struct VB1 : virtual A { virtual void f(); };
struct VB2 : virtual A { virtual void f(); };
struct Error : VB1, VB2 { }; // no unique final overrider
struct OKay : VB1, VB2 { void f(); }; // ok


--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: stephen.clamage@sun.com (Steve Clamage)
Date: 1998/12/10
Raw View
Stefan Rupp <struppi@gia.rwth-aachen.de> writes:

>> A defensible design rule is that a virtual base class should have
>> no data at all, and thus no declared constructors. The default
>> constructor is then implicit and public.

>But if a base class does not have any data members, why should I make
>it virtual? Does that make any sense?

Under this view, the base class defines only an interface. If
the base class is not virtual, calls to the member functions
under multiple inheritance may be ambiguous:

class Base { ... virtual int f(); ... };
class A : public Base { ... };
class B : public Base { ... };
class C : public A, public B { ... };

int main()
{
    C c;
    c.f(); // ambiguous
}

Change A and B to inherit Base virtually and the call is not ambiguous.

Another reason for virtual base classes not to have data is the
problem of writing assignment operators. The compiler-generated
assignment operator is not guaranteed by the standard to do the
right thing (because in general it is too difficult to determine
what the right thing is). Tom Cargill in "C++ Programming Style"
(Addison-Wesley, 1992) devotes much of chapter 9 to a discussion
of assignment operators in the presence of virtual base classes.

If a virtual base class has no data, this problem also goes away.

--
Steve Clamage, stephen.clamage@sun.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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Marc Girod <girod@stybba.ntc.nokia.com>
Date: 1998/12/10
Raw View
>>>>> "SR" == Stefan Rupp <struppi@gia.rwth-aachen.de> writes:

SR> But if a base class does not have any data members, why should I make
SR> it virtual? Does that make any sense?

Of course: using mixins, i.e. getting the implementation from
siblings.

Best Regards!
Marc

--
Marc Girod                Hiomo 5/1          Voice:  +358-9-511 23746
Nokia Telecommunications  P.O. Box 320       Mobile: +358-40-569 7954
NWS/NMS/NMS for Data      00045 NOKIA Group  Fax:    +358-9-511 23580
                          Finland            marc.girod@ntc.nokia.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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Jeff Greif" <jmg@spam-me-not.trivida.com>
Date: 1998/12/08
Raw View
Dietmar Kuehl pointed out this part of the standard to me in connection with
basic_ostream subclasses.  It seems to raise several difficulties for class
designers.  I haven't found earlier discussions of this issue, and hope
someone can clarify the rationale for this section.

Paragraph 12.6.2.6 (of CD2 working paper) says:
All sub-objects representing virtual base classes are  initialized  by
the  constructor  of  the most derived class (_intro.object_).  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.  A mem-initializer  naming  a  virtual  base  class  shall  be
ignored  during  execution of the constructor of any class that is not
the most derived class.

Presumably, one reason for this is that the most derived class might be one
which is merging two inheritance paths leading to the virtual base and so
must resolve the ambiguity.

Paragraph 12.6.2.6 seems to imply that if V is a virtual base of A with no
default constructor in the hierarchy below,

V <- A <- B <- C ...<- J <- K

that each class B,...K must initialize V explicitly.  This seems to require
that the designer of K, which might be a trivial specialization of J (for
example, setting some data member of J to a fixed value), must inspect the
entire hierarchy to determine if there is some virtual base class that must
be initialized.  (Presumably she would get a hint by looking at the
mem-initializers of J and seeing something mysterious.)  This seems to place
an unreasonable burden on the designer of higher-level classes.

Suppose the only constructor of V is of the form V(X*).  The class B might
be the most general class in the chain which actually needed to manipulate
an X, so it might have a private member which is an X, and pass the address
of that member to the initializer of V that it uses in its constructor's
mem-initializers.  However, if this member of B is private, none of the
derived classes of B can obey the strictures of 12.6.2.6, unless B provides
a protected or public accessor to that member, and all the derived classes
use it.  This seems to violate the principle of encapsulation.

How does 12.6.2.6 apply when one of the classes, say C, is privately derived
from its superclass B?  Surely C and its subclasses cannot initialize V in
their constructors without violating the access restrictions.  Does that
mean they are ill-formed, or does some other section of the standard take
precedence?  If they are ill-formed, then it is illegal to privately derive
from any class with a virtual base that has no default constructor.

Unless I'm missing something, there are no initialization ambiguities to be
resolved when deriving from a class with virtual base unless the derived
class has explicit multiple inheritance that might provide another
inheritance path to the virtual base classes in question.  A class derived
from a single direct superclass could without ambiguity rely on its
superclass' initialization of virtual bases.  I suppose if the standard
allowed this, there would be the potential for mystification when deriving
from two such classes that give no evidence that they contain a virtual
base.  Is that the reason the standard does not allow this?

Similarly, a class derived from several superclasses could rely on the
superclasses' initialization of the virtual bases if the inheritance did not
introduce several paths to the same virtual base.  This requirement is much
harder to state and motivate, but I suspect the reason for not allowing
dependence on superclasses' initialization of the virtual bases is simliar
to that for the single inheritance case.  Does anyone know the rationale for
this?

Finally, at least two compilers (MS VC++5.0 SP2, Sun SC5.0 pre-release) do
not enforce this paragraph of the standard, since it does not object to the
sequence of classes
virtual basic_ios <- basic_ostream <- basic_ofstream
in which basic_ios has no default constructor, basic_ostream initializes
basic_ios with the streambuf* it requires, and basic_ofstream does nothing
about the virtual base.  In the Sun iostream library, even basic_ostream
does not initialize the basic_ios.

Jeff




[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: stephen.clamage@sun.com (Steve Clamage)
Date: 1998/12/08
Raw View
"Jeff Greif" <jmg@spam-me-not.trivida.com> writes:

>I haven't found earlier discussions of this issue, and hope
>someone can clarify the rationale for this section.

>Paragraph 12.6.2.6 (of CD2 working paper) says:
>All sub-objects representing virtual base classes are  initialized  by
>the  constructor  of  the most derived class (_intro.object_).  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.  A mem-initializer  naming  a  virtual  base  class  shall  be
>ignored  during  execution of the constructor of any class that is not
>the most derived class.

That has always been the rule in C++. The standard breaks no new
ground here.

>Presumably, one reason for this is that the most derived class might be one
>which is merging two inheritance paths leading to the virtual base and so
>must resolve the ambiguity.

Yes.

>Paragraph 12.6.2.6 seems to imply that if V is a virtual base of A with no
>default constructor in the hierarchy below,

>V <- A <- B <- C ...<- J <- K

>that each class B,...K must initialize V explicitly.

Bear in mind that virtual inheritance is pointless except in
the case of multiple inheritance, and in that case, you have
the problem of who initializes the virtual base. Hence the
language rule.

> This seems to require
>that the designer of K, which might be a trivial specialization of J (for
>example, setting some data member of J to a fixed value), must inspect the
>entire hierarchy to determine if there is some virtual base class that must
>be initialized.

Or wait for the compiler to complain. :-)

> This seems to place
>an unreasonable burden on the designer of higher-level classes.

Yes. Consequently, you should avoid using virtual base classes that
require special initialization. If the class has only a public
default constructor, the problem goes away.

A defensible design rule is that a virtual base class should have
no data at all, and thus no declared constructors. The default
constructor is then implicit and public.

Your original question came from iostreams, which violate
this design rule. OTOH, you don't normally plunk an istream
or ostream down into a hierarchy. Normally you would derive
a single level to create a new kind of stream (fstreams
and stringstreams are examples). The buffer classes of iostreams
use single inheritance and do not have virtual base classes.

If you create your own iostream classes, you have a host of
details to master. Knowing about the buffer argument to the
virtual base class is the smallest of your problems.

--
Steve Clamage, stephen.clamage@sun.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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Stefan Rupp <struppi@gia.rwth-aachen.de>
Date: 1998/12/09
Raw View
Good morning,

stephen.clamage@sun.com (Steve Clamage) writes:
> Yes. Consequently, you should avoid using virtual base classes that
> require special initialization. If the class has only a public
> default constructor, the problem goes away.

This advice sounds reasonable.

> A defensible design rule is that a virtual base class should have
> no data at all, and thus no declared constructors. The default
> constructor is then implicit and public.

But if a base class does not have any data members, why should I make
it virtual? Does that make any sense?

Doei,
     struppi

--
Dipl.-Inform. Stefan Rupp                      Email: struppi@acm.org
Geodaetisches Institut der RWTH Aachen         Tel.:  +49 241 80-5295
Templergraben 55, D-52062 Aachen, Germany      Fax:  +49 241 8888-142


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]