Topic: Virtual constructors


Author: postmast.root.admi.gov@iname.com (blargg)
Date: 1999/09/18
Raw View
In article <_U9E3.15232$0M6.334017@news1.rdc2.on.home.com>, "John Smith"
<jraft@home.com> wrote:

> > You mean a compiler generated clone function?  Should it be called "clone"
> > or "Clone"?  What does Base::clone() or Base::Clone() return?  Perhaps a
> > "Base *" or perhaps a std::auto_ptr<Base> or perhaps something else.  And
> > how about Derived::clone().
>
> I think your confused but others will recognize what I'm talking about.
> Constructors can't be made virtual. So if I call the copy constructor on a
> pointer to some base class that really points to a derived class, the copy
> constructor gets called for the base class instead of the derived class.

Back up. When did you determine that you could call constructors?

> Now
> your in trouble because the derived class' copy constructor (and any of its
> other base classes where multiple inheritance is involved) don't get called.
> This occurs because the (base) class constructor isn't virtual (the language
> disallows it). You therefore have to provide your own virtual  "Clone()"
> function to be called instead of the copy constructor in these cases. The
> "Clone()" function (as it's traditionally called) in the derived class will
> therefore get called (because it's virtual) and it in turn can invoke its
> own copy constructor (implementing the correct and intended behaviour).

clone() says:

    allocate a new object on the freestore, of the same complete type as
    this one, that is a copy of this object

copy ctor:

    special function called when constructing this sub-object in raw memory,
    where the complete type is possibly a derived type

I don't see that merging these will yield anything useful.

> I'll
> leave it to others to volunteer a quick example of this (typically using
> "new" to copy an object). My question is why can't constructors be made
> virtual?

They *construct* an object from raw bits. They are *not* member functions,
and are *not* callable by the user.

> Stroustrup briefly discusses it in his book but I'm a little foggy
> still. Providing your own clone functions is irritating

"This is irritating! Let's mutate something else to make this non-irritating!"

Irritating is a good reason. So, let's make clone() easier to provide. As
Francis G. suggested, it would be useful to have someone implement
required virtual functions (they must be overridden in *every* class) in
their compiler.

> and in any event, in
> the absence of such functions (by accident or by the less experienced), the
> original problem I described above won't get caught at compile time and can
> therefore be a notorious source of errors (your derived class won't get
> initialized as described).

Yes, it is error-prone.

But how hard is it to catch in your test module?

    template<typename T>
    void check_clone() {
        assert( typeid ((new T)->clone()) == typeid (T) );
    }

    int main() {
        check_clone<Foo>();
        check_clone<Bar>();
        // ...
    }
---
[ 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: postmast.root.admi.gov@iname.com (blargg)
Date: 1999/09/18
Raw View
In article <37e13a1b.0@news.pacifier.com>, "Marco Dalla Gasperina"
<marcodg@pacifier.com> wrote:

[snip]
> There is also the issue of what is meant by 'clone.'
> It could mean:
>
> (1) A virtual copy-constructor.
>     x = y->clone() implies *x == *y
>
> (2) A virtual default constructor.
>     x = y->clone() implies x = new dynamic-type-of-y();
>
> (3) Something in-between.

I like "new_same_type()" or similar for (2). clone() I think is quite
clear - make a clone of the object. Of course, clone will depend on the
semantics of the object, but should be implemented with the copy ctor (if,
for no other reason, that the copy ctor automatically calls base class
copy ctors).

> I'd always thought that type_info/type_id could be extended to
> allow something like
>
>     x = type_construct( const type_info& ti );
>
> requiring the final type to have a default constructor (throwing
> an exception if it didn't) to give a result something like (2).

That could be useful, but it has too many problems:

How is the object allocated? new T?

What type does type_construct return? (void*? useless, but what else could
it know of?)

What if the type has no default ctor? (run-time failure?)


You can get this behavior by using a map from type_info to a function that
creates the object, but you'll still have to solve the above problems.
---
[ 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: Valentin Bonnard <Bonnard.V@wanadoo.fr>
Date: 1999/09/16
Raw View
John Smith wrote:

[ Why there isn't xxx in C++. ]

Things don't work this way; first propose a specific feature,
with its semantic, how it should be used, etc. Then people in
this newsgroup will argue that your proposal is brain-dead.
You can also get some supporters. Then you reformulate your
idea in a better way, etc.

But first, you have to write a description of the feature; since
you haven't done that, we have nothing to criticize/support.

But vague wills (like ``what about clone ?'') lead nowhere.

--

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






Author: "Marco Dalla Gasperina" <marcodg@pacifier.com>
Date: 1999/09/16
Raw View

Steve Clamage <clamage@eng.sun.com> wrote in message
news:7rr81j$k8q$1@engnews1.eng.sun.com...
>
> sbnaran@uiuc.edu (Siemel B. Naran) writes:
>
> >On 15 Sep 99 18:21:25 GMT, John Smith <jraft@home.com> wrote:
>
> >>Does anybody know why these weren't introduced into the language.
Stroustrup
> >>only makes brief mention of it in his book. No doubt there's good reason
for
> >>it but writing "Clone()" functions all the time is rather annoying
(albeit
> >>short and clean).
>
> >You mean a compiler generated clone function?  Should it be called
"clone"
> >or "Clone"?  What does Base::clone() or Base::Clone() return?  Perhaps a
> >"Base *" or perhaps a std::auto_ptr<Base> or perhaps something else.  And
> >how about Derived::clone().
>
> I don't see any of those questions as problematic.
>
> The main problem I see is that a clone function should be virtual.
> A compiler-generated non-virtual clone function would be source
> of subtle program bugs.

There is also the issue of what is meant by 'clone.'
It could mean:

(1) A virtual copy-constructor.
    x = y->clone() implies *x == *y

(2) A virtual default constructor.
    x = y->clone() implies x = new dynamic-type-of-y();

(3) Something in-between.

I'd always thought that type_info/type_id could be extended to
allow something like
    x = type_construct( const type_info& ti );

requiring the final type to have a default constructor (throwing
an exception if it didn't) to give a result something like (2).

marco




[ 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: "John Smith" <jraft@home.com>
Date: 1999/09/17
Raw View
> You mean a compiler generated clone function?  Should it be called "clone"
> or "Clone"?  What does Base::clone() or Base::Clone() return?  Perhaps a
> "Base *" or perhaps a std::auto_ptr<Base> or perhaps something else.  And
> how about Derived::clone().

I think your confused but others will recognize what I'm talking about.
Constructors can't be made virtual. So if I call the copy constructor on a
pointer to some base class that really points to a derived class, the copy
constructor gets called for the base class instead of the derived class. Now
your in trouble because the derived class' copy constructor (and any of its
other base classes where multiple inheritance is involved) don't get called.
This occurs because the (base) class constructor isn't virtual (the language
disallows it). You therefore have to provide your own virtual  "Clone()"
function to be called instead of the copy constructor in these cases. The
"Clone()" function (as it's traditionally called) in the derived class will
therefore get called (because it's virtual) and it in turn can invoke its
own copy constructor (implementing the correct and intended behaviour). I'll
leave it to others to volunteer a quick example of this (typically using
"new" to copy an object). My question is why can't constructors be made
virtual? Stroustrup briefly discusses it in his book but I'm a little foggy
still. Providing your own clone functions is irritating and in any event, in
the absence of such functions (by accident or by the less experienced), the
original problem I described above won't get caught at compile time and can
therefore be a notorious source of errors (your derived class won't get
initialized as described).
---
[ 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: Ron Natalie <ron@sensor.com>
Date: 1999/09/17
Raw View
Steve Clamage wrote:
>
>
> The main problem I see is that a clone function should be virtual.
> A compiler-generated non-virtual clone function would be source
> of subtle program bugs.
>

I think that in order to have a virtual constructor, you'd have
to get over the concept that you can't call constuctors at all
in C++.

If:

 struct A { ... };
 struct B : A { ... };

 A* a = new B;

 A* a2 = a->A();

were legal (along the lines that a->~A() is now) then an
optional virtual keyword on the constructor definition
similar to the way you can on the destructor would work.
---
[ 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: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1999/09/17
Raw View
John Smith wrote:
>
> > You mean a compiler generated clone function?  Should it be called "clone"
> > or "Clone"?  What does Base::clone() or Base::Clone() return?  Perhaps a
> > "Base *" or perhaps a std::auto_ptr<Base> or perhaps something else.  And
> > how about Derived::clone().
>
> I think your confused but others will recognize what I'm talking about.
> Constructors can't be made virtual. So if I call the copy constructor on a
> pointer to some base class that really points to a derived class, the copy

You don't call the copy constructor on a pointer to a derived class.
You call the constructor on raw memory (or rather, the compiler
does that, since you can't call the constructor directly at all).
It may receive a reference to a derived class as parameter, though.

> constructor gets called for the base class instead of the derived class. Now
> your in trouble because the derived class' copy constructor (and any of its
> other base classes where multiple inheritance is involved) don't get called.

What if the derived object would need more space than was
allocated?
"Virtual construction" only works well if it is combined
with dynamic memory allocation (where the real type
determines the amount of memory to be allocated). This can't
work with ordinary constructors (which just take some memory,
and construct the object in there).
One could add new syntax for a "dynamic new" or clone
operator, which uses the dynamic type of a given object
to decide how much memory to allocate (and which allocation
function to call), and then uses this object's copy
constructor to create a new one.

> This occurs because the (base) class constructor isn't virtual (the language
> disallows it).

No. When you call the copy constructor, it's far too late.
You must know the type to create _before_ calling the
constructor, just to make sure there's sufficient memory
allocated for it.

> You therefore have to provide your own virtual  "Clone()"
> function to be called instead of the copy constructor in these cases. The
> "Clone()" function (as it's traditionally called) in the derived class will
> therefore get called (because it's virtual) and it in turn can invoke its
> own copy constructor (implementing the correct and intended behaviour). I'll
> leave it to others to volunteer a quick example of this (typically using
> "new" to copy an object).

This gives the crucial hint: The clone function doesn't just
copy, it _allocates_ and copies. Therefore it's not just a
virtual constructor, but rather a sort of virtual new.

> My question is why can't constructors be made
> virtual?

Because virtual functions are dispatched by the type of the
object they are called on. But constructors are _not_ called
on objects, but on raw memory. And for cloning, you don't even
have that raw memory, because you can't allocate it before you
know how much you need.

> Stroustrup briefly discusses it in his book but I'm a little foggy
> still. Providing your own clone functions is irritating and in any event, in
> the absence of such functions (by accident or by the less experienced), the
> original problem I described above won't get caught at compile time and can
> therefore be a notorious source of errors (your derived class won't get
> initialized as described).

That's indeed a problem, but IMHO virtual constructors are not
the correct solution (at least not for the C++ object model;
in languages with a purely dynamic object model, where allocating
heap memory is an inherent part of creating the object, it may
be the correct solution).

Imagine, you'd have a virtual copy constructor which
some way works together with new (maybe the compiler
checks for that in new expressions and determines the
correct size at runtime). That is, the following would
create d Derived object, if the pointer points to one:

class Base
{
  int i;
public:
  virtual Base(Base const& other): i(other.i) {}
  // ...
};

class Derived: public Base { int j; /* ... */ };

Base* clone(Base* p)
{
  return new Base(*p);
}

Now consider the following class:

class OtherClass
{
  Base b1, b2;
public:
  OtherClass(Base& b): b1(b), b2(b) {}
};

Now what should happen on constructon of OtherClass, if the
passed object is actually a Derived? The base copy constructor
is virtual, so - given the semantics derived from new - the
derived copy constructor should be called. But the objects
b1 and b2 are really Base objects - what does it mean to
call a Derived constructor on a Base object?
Or should b1 and b2 be made Derived objects instead, if
b refers to a Derived? But there is not enough memory to
put a Derived into b1 and b2 (there's just enough for Base).
Or should the size of b1 and b2 depend on the real type of b?
And therefore the size of any OtherClass object as well?


[ 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: "John Smith" <jraft@home.com>
Date: 1999/09/15
Raw View
Does anybody know why these weren't introduced into the language. Stroustrup
only makes brief mention of it in his book. No doubt there's good reason for
it but writing "Clone()" functions all the time is rather annoying (albeit
short and clean).
---
[ 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@uiuc.edu (Siemel B. Naran)
Date: 1999/09/16
Raw View
On 15 Sep 99 18:21:25 GMT, John Smith <jraft@home.com> wrote:

>Does anybody know why these weren't introduced into the language. Stroustrup
>only makes brief mention of it in his book. No doubt there's good reason for
>it but writing "Clone()" functions all the time is rather annoying (albeit
>short and clean).

You mean a compiler generated clone function?  Should it be called "clone"
or "Clone"?  What does Base::clone() or Base::Clone() return?  Perhaps a
"Base *" or perhaps a std::auto_ptr<Base> or perhaps something else.  And
how about Derived::clone().

--
--------------
siemel b naran
--------------


[ 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: clamage@eng.sun.com (Steve Clamage)
Date: 1999/09/16
Raw View
sbnaran@uiuc.edu (Siemel B. Naran) writes:

>On 15 Sep 99 18:21:25 GMT, John Smith <jraft@home.com> wrote:

>>Does anybody know why these weren't introduced into the language. Stroustrup
>>only makes brief mention of it in his book. No doubt there's good reason for
>>it but writing "Clone()" functions all the time is rather annoying (albeit
>>short and clean).

>You mean a compiler generated clone function?  Should it be called "clone"
>or "Clone"?  What does Base::clone() or Base::Clone() return?  Perhaps a
>"Base *" or perhaps a std::auto_ptr<Base> or perhaps something else.  And
>how about Derived::clone().

I don't see any of those questions as problematic.

The main problem I see is that a clone function should be virtual.
A compiler-generated non-virtual clone function would be source
of subtle program bugs.

For compatibility with C structs, no class should by default have
any virtual functions. That's why destructors are not virtual
by default. (A class has no virtual functions unless it or a
base class contains an explicit virtual function declaration.)

--
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              ]