Topic: On clone() and deriving from template of s
Author: Edward Diener <eddielee@abraxis.com>
Date: 1998/02/19 Raw View
You've thought about this more deeply than I have. I forgot about the
possible confusion with the "new" placement syntax. I would also favor
more limiting rules (no friend and the copy constructor must be public)
since the burden of creating a public copy constructor is trivial and
if it is not, and the default copy constructor is disallowed via a
protected or private do-nothing implementation, then obviously the
designer of the polymorphic class does not want cloning to be applied.
I also don't like the idea of a "virtual" constructor as this seems to
needlessly complicate the language. Also the cloning syntax of "new
(T)" where T is some type rather than an object does not seem
worthwhile to me since what we are talking about is the cloning of the
actual polymorphic object. I would rather "new (x)" always be based on
a polymorphic object x . These are just my own opinions.
Another syntactical problem to think about is if someone wants to do
cloning with a placement syntax, as inevitably people might want to do.
If the syntax could be worked out and proved to definitely not conflict
in any token sequence with the "new" placement operator so that it is
parsible by compilers, I would definitely like to see this as part of
some future enhancement of the language.
Even if the syntax appears to become too abstruse an attempt to provide
a cloning ability that is orthogonal to the "new" syntax, a clean
Gordian knot solution is to just specify a new keyword "clone" to be
used similarly "new" is used, and whose functionality is the
polymorphic "new" we have been discussing.
You are right that there is much more to think about but a cloning
solution based on the language is an idea that I like.
Christopher Eltschka wrote:
> Edward Diener wrote:
> >
> > I agree that with language support it would be much easier. Your idea is a good
> > one. Maybe in 5 years but yes it's too late now. However I believe their is a
> > process for submitting your idea for the next 5 year changes. It does seem
> > implementable by using the same technique as vtables to call the copy
> > constructor of the actual object to make a clone and placing that object in
> > dynamic memory. With 'type' the compiler can make a temporary object on the
> > stack via a default constructor and then use the same technique. However in the
> > latter case suppose the instantiation of the object of a given type does not
> > have a default constructor, then the syntax would have to fail in that case,
> > ie.
> >
> > class X
> > {
> >
> > public:
> >
> > X(int i) {...}
> >
> > };
> >
> > then the expression 'new (X)' should fail.
>
> I don't think the default constructor would be any problem. Even today,
> new X fails if X has no (accessible) default constructor. The more
> important point is the copy constructor. If there's no (accessible)
> copy constructor for a type, new (object) should fail. However,
> new Type; should of course work as expected, even if no copy constructor
> is available. And how should you treat new X(a)? Is this a copy of
> a temporary object initialized with a? Or is it just a new object
> initialized with a? Maybe you could make a rule that you must put
> parentheses around an object. Then new X(a) would be an old-fashioned
> new expression, while new (X(a)) would create a clone of a temporary
> object, therefore requiring the copy constructor.
>
> You would also need to consider placement new (which again has
> parameters - that is, objects - in parentheses). However, you
> could find out which is meant by simply counting the expressions:
>
> new X(a); // old style normal new
> new (p) X(a); // old style placement new
> new (x); // cloning normal new
> new (p) (x); // cloning placement new
>
> One more important problem is the availability of the copy constructor:
>
> class A
> {
> virtual ~A();
> };
>
> class B: public A // no copy constructor!
> {
> int& i;
> public:
> B(int& ii): i(ii) {}
> };
>
> class C: public A
> {
> friend void g(A*);
> C(const C&); // private!
> public:
> C();
> };
>
> A* f(A* a)
> {
> return new (*a);
> }
>
> A* g(A* a)
> {
> return new (*a);
> }
>
> int main()
> {
> B b;
> C c;
> A* a1=f(&b); // b doesn't have a copy constructor!
> A* a2=f(&c); // C's copy constructor is private!
> A* a3=g(&c); // C's copy constructor is private, but g is friend!
> delete a1;
> delete a2;
> delete a3;
> }
>
> As you see, there are at least three problems to solve:
>
> 1. How to treat non-existant copy constructors
> This is the easiest; a NULL pointer or an exception would work here.
> The non-existance is easy to detect at runtime.
>
> 2. How to treat private/protected constructors.
> The easiest solution would be to treat them as nonexistant, but
> this would mean that you couldn't call them from friends (see 3)
>
> 3. How to treat friend/non-friend functions.
> Given that the exact type is not known at compile time, the
> compiler cannot check if a function is friend. Moreover, the
> class definition could even be unknown at the point of the
> cloning new expression. Also, unlike plain existance, there's
> no simple way to determine friendship at runtime, as this involves
> knowledge about the class as well as about the function.
>
> Example:
>
> File 1:
>
> #include "A.h"
>
> class X: public A
> {
> friend A* f(A*);
> A(const A&); // private!
> // ...
> };
>
> File 2:
>
> #include "A.h"
>
> A* f(A* a)
> {
> return new (*a);
> }
>
> Now the compiler doesn't know that A* f(A*) is friend of X,
> it doesn't even know that X exists (it might not have existed
> when File 2 was compiled).
>
> The simplest solution of course would be to completely ignore
> friendship for cloning. OTOH, this is somewhat unsatisfying.
> Another solution would be to treat the copy constructor as
> accessible from anywhere where the base copy constructor
> (A::A(const A&) in the example above) is accessible. This
> would somewhat mimic the treatment of virtual functions.
> Maybe it should be required to declare the copy constructor
> virtual if cloning is desired (thus making an exception from
> the rule that constructors may not be virtual). For objects
> without virtual functions, cloning should be disabled anyway
> (as it needs a virtual vtbl), instead the base class copy
> constructor should be called. An explicitly virtual copy
> constructor would probably make this more orthogonal to
> other members.
>
> The addition of cloning new is obviously not as simple as
> it seems at the first view...
---
[ 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: Edward Diener <eddielee@abraxis.com>
Date: 1998/02/10 Raw View
Cloning isn't fundamental to C++ but if you are going to have a container of pointers to polymorphic
base class objects, and you want to create another container of different pointers to those same base
class objects, you need a clone function for each object. As an example:
std::vector<Base *> container;
Now you want to copy each of those objects to another container via their pointers you must do
something like:
std::vector<Base *> containerCopy;
std::vector::iterator it;
for (it = container.begin(); it != container.end(); ++it)
{
Base * & orig = *it;
Base * c = orig -> Clone();
containerCopy.push_back(c);
}
I find this need to clone objects in polymorphic containers to be pretty important so I always stick in
a Clone virtual function in my own class hierarchies. The Clone function simply uses the copy
constructor to make a copy of itself. However I don't see that this should be built in to the language
but I find it an interesting thought.
Steve Clamage wrote:
> In article 5331F9E@math.princeton.edu, "Marc-Andri lafortune" <lafortne@math.Princeton.EDU> writes:
> >Before posting this question, I went to the book store and bought a copy of
> >Stroustrup's 'Desing and Evolution of C++' ("D&E").
>
> My hat's off to you! I wish more people would follow the advice in
> the FAQ.
>
> >A) Why aren't objects cloneable?
> >
> > One of my first surprise with C++ was the absence of cloning. In C, you can
> >always make a copy of any type (even if reference through a pointer). Because
> >of inheritance, this is no longer possible without risk of slicing.
> > I don't quite see why classes wouldn't have a default clone() defined; a
> >class could modify it, or forbid it by making it private. It looks to me as
> >pretty similar to the default copy constructor.
>
> The copy constructor makes a copy of the object. The default is a shallow
> copy, but you can change the semantics by providing your own copy
> constructor. It seems to me that this performs a clone operation
> on the object.
>
> But you mentioned slicing, so perhaps you want a virtual clone
> operation. You can easily write your own. For a class T, it could look
> like either of these:
>
> class T {
> public: // pick one of these
> virtual T& clone() const { return *new T(*this); }
> virtual T* clone() const { return new T(*this); }
> ...
> };
>
> You would get shallow or deep or some other kind of cloning depending
> on T's copy constructor.
>
> If class U is derived from T, its clone function would look the same
> except that it would return a U& or U*. That is possible now because
> C++ now supports "covariant return types". C++ did not originally
> have covariant return types, so a virtual clone function would have
> had to return a pointer to the most-base class. In the presence of
> multiple inheritance, that wouldn't always be possible.
> class A { ... virtual A* clone(); };
> class B { ... virtual B* clone(); };
> class C : public A, public B { ??? };
> Even without the multiple inheritance problem, returning a pointer
> to the base class is not ideal.
>
> Those reasons I think are why C++ did not have a clone operation:
> If it were possible to do at all, it was easy for the programmer
> to do it just as well as the compiler could do it.
>
> Why wasn't clone added later? I don't remember anyone ever suggesting
> it. It is still easy to write your own. In addition, I don't see
> cloning as fundamental to C++, although it may be fundamental in
> other languages. In particular, C++ does not use the heap unless
> you ask for it explicitly, unlike other OO languages.
>
> ---
> 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 ]
---
[ 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/std-c++/faq.html ]
Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1998/02/11 Raw View
Edward Diener wrote:
>
> Cloning isn't fundamental to C++ but if you are going to have a
> container of pointers to polymorphic base class objects, and you want
> to create another container of different pointers to those same base
> class objects, you need a clone function for each object. As an
> example:
>
> std::vector<Base *> container;
>
> Now you want to copy each of those objects to another container via their
> pointers you must do something like:
>
> std::vector<Base *> containerCopy;
> std::vector::iterator it;
>
> for (it = container.begin(); it != container.end(); ++it)
> {
> Base * & orig = *it;
> Base * c = orig -> Clone();
> containerCopy.push_back(c);
> }
>
> I find this need to clone objects in polymorphic containers to be
> pretty important so I always stick in a Clone virtual function in my
> own class hierarchies. The Clone function simply uses the copy
> constructor to make a copy of itself. However I don't see that this
> should be built in to the language but I find it an interesting
> thought.
It would be nice to have language support for this.
A possibility would be to allow objects as well
as typenames in new expressions, for example:
class Base { irtual ~Base(); };
class Derived: public Base {};
int main()
{
Base* pB=new Derived; // create a derived object
Base* pB2=new (*pB); // clone *pB
}
Your container loop would then simply look like this:
for (it = container.begin(); it != container.end(); ++it)
{
containerCopy.push_back(new (*it));
}
BTW, it's too late now for this (or maybe way too early?)
[...]
---
[ 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: Edward Diener <eddielee@abraxis.com>
Date: 1998/02/12 Raw View
I agree that with language support it would be much easier. Your idea is a good
one. Maybe in 5 years but yes it's too late now. However I believe their is a
process for submitting your idea for the next 5 year changes. It does seem
implementable by using the same technique as vtables to call the copy
constructor of the actual object to make a clone and placing that object in
dynamic memory. With 'type' the compiler can make a temporary object on the
stack via a default constructor and then use the same technique. However in the
latter case suppose the instantiation of the object of a given type does not
have a default constructor, then the syntax would have to fail in that case,
ie.
class X
{
public:
X(int i) {...}
};
then the expression 'new (X)' should fail.
Christopher Eltschka wrote:
> Edward Diener wrote:
> >
> > Cloning isn't fundamental to C++ but if you are going to have a
> > container of pointers to polymorphic base class objects, and you want
> > to create another container of different pointers to those same base
> > class objects, you need a clone function for each object. As an
> > example:
> >
> > std::vector<Base *> container;
> >
> > Now you want to copy each of those objects to another container via their
> > pointers you must do something like:
> >
> > std::vector<Base *> containerCopy;
> > std::vector::iterator it;
> >
> > for (it = container.begin(); it != container.end(); ++it)
> > {
> > Base * & orig = *it;
> > Base * c = orig -> Clone();
> > containerCopy.push_back(c);
> > }
> >
> > I find this need to clone objects in polymorphic containers to be
> > pretty important so I always stick in a Clone virtual function in my
> > own class hierarchies. The Clone function simply uses the copy
> > constructor to make a copy of itself. However I don't see that this
> > should be built in to the language but I find it an interesting
> > thought.
>
> It would be nice to have language support for this.
> A possibility would be to allow objects as well
> as typenames in new expressions, for example:
>
> class Base { irtual ~Base(); };
> class Derived: public Base {};
>
> int main()
> {
> Base* pB=new Derived; // create a derived object
> Base* pB2=new (*pB); // clone *pB
> }
>
> Your container loop would then simply look like this:
>
> for (it = container.begin(); it != container.end(); ++it)
> {
> containerCopy.push_back(new (*it));
> }
>
> BTW, it's too late now for this (or maybe way too early?)
[ 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: 1998/02/16 Raw View
Edward Diener wrote:
>
> I agree that with language support it would be much easier. Your idea is a good
> one. Maybe in 5 years but yes it's too late now. However I believe their is a
> process for submitting your idea for the next 5 year changes. It does seem
> implementable by using the same technique as vtables to call the copy
> constructor of the actual object to make a clone and placing that object in
> dynamic memory. With 'type' the compiler can make a temporary object on the
> stack via a default constructor and then use the same technique. However in the
> latter case suppose the instantiation of the object of a given type does not
> have a default constructor, then the syntax would have to fail in that case,
> ie.
>
> class X
> {
>
> public:
>
> X(int i) {...}
>
> };
>
> then the expression 'new (X)' should fail.
I don't think the default constructor would be any problem. Even today,
new X fails if X has no (accessible) default constructor. The more
important point is the copy constructor. If there's no (accessible)
copy constructor for a type, new (object) should fail. However,
new Type; should of course work as expected, even if no copy constructor
is available. And how should you treat new X(a)? Is this a copy of
a temporary object initialized with a? Or is it just a new object
initialized with a? Maybe you could make a rule that you must put
parentheses around an object. Then new X(a) would be an old-fashioned
new expression, while new (X(a)) would create a clone of a temporary
object, therefore requiring the copy constructor.
You would also need to consider placement new (which again has
parameters - that is, objects - in parentheses). However, you
could find out which is meant by simply counting the expressions:
new X(a); // old style normal new
new (p) X(a); // old style placement new
new (x); // cloning normal new
new (p) (x); // cloning placement new
One more important problem is the availability of the copy constructor:
class A
{
virtual ~A();
};
class B: public A // no copy constructor!
{
int& i;
public:
B(int& ii): i(ii) {}
};
class C: public A
{
friend void g(A*);
C(const C&); // private!
public:
C();
};
A* f(A* a)
{
return new (*a);
}
A* g(A* a)
{
return new (*a);
}
int main()
{
B b;
C c;
A* a1=f(&b); // b doesn't have a copy constructor!
A* a2=f(&c); // C's copy constructor is private!
A* a3=g(&c); // C's copy constructor is private, but g is friend!
delete a1;
delete a2;
delete a3;
}
As you see, there are at least three problems to solve:
1. How to treat non-existant copy constructors
This is the easiest; a NULL pointer or an exception would work here.
The non-existance is easy to detect at runtime.
2. How to treat private/protected constructors.
The easiest solution would be to treat them as nonexistant, but
this would mean that you couldn't call them from friends (see 3)
3. How to treat friend/non-friend functions.
Given that the exact type is not known at compile time, the
compiler cannot check if a function is friend. Moreover, the
class definition could even be unknown at the point of the
cloning new expression. Also, unlike plain existance, there's
no simple way to determine friendship at runtime, as this involves
knowledge about the class as well as about the function.
Example:
File 1:
#include "A.h"
class X: public A
{
friend A* f(A*);
A(const A&); // private!
// ...
};
File 2:
#include "A.h"
A* f(A* a)
{
return new (*a);
}
Now the compiler doesn't know that A* f(A*) is friend of X,
it doesn't even know that X exists (it might not have existed
when File 2 was compiled).
The simplest solution of course would be to completely ignore
friendship for cloning. OTOH, this is somewhat unsatisfying.
Another solution would be to treat the copy constructor as
accessible from anywhere where the base copy constructor
(A::A(const A&) in the example above) is accessible. This
would somewhat mimic the treatment of virtual functions.
Maybe it should be required to declare the copy constructor
virtual if cloning is desired (thus making an exception from
the rule that constructors may not be virtual). For objects
without virtual functions, cloning should be disabled anyway
(as it needs a virtual vtbl), instead the base class copy
constructor should be called. An explicitly virtual copy
constructor would probably make this more orthogonal to
other members.
The addition of cloning new is obviously not as simple as
it seems at the first view...
[ 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: 1998/02/06 Raw View
In article 5331F9E@math.princeton.edu, "Marc-Andri lafortune" <lafortne@math.Princeton.EDU> writes:
>Before posting this question, I went to the book store and bought a copy of
>Stroustrup's 'Desing and Evolution of C++' ("D&E").
My hat's off to you! I wish more people would follow the advice in
the FAQ.
>A) Why aren't objects cloneable?
>
> One of my first surprise with C++ was the absence of cloning. In C, you can
>always make a copy of any type (even if reference through a pointer). Because
>of inheritance, this is no longer possible without risk of slicing.
> I don't quite see why classes wouldn't have a default clone() defined; a
>class could modify it, or forbid it by making it private. It looks to me as
>pretty similar to the default copy constructor.
The copy constructor makes a copy of the object. The default is a shallow
copy, but you can change the semantics by providing your own copy
constructor. It seems to me that this performs a clone operation
on the object.
But you mentioned slicing, so perhaps you want a virtual clone
operation. You can easily write your own. For a class T, it could look
like either of these:
class T {
public: // pick one of these
virtual T& clone() const { return *new T(*this); }
virtual T* clone() const { return new T(*this); }
...
};
You would get shallow or deep or some other kind of cloning depending
on T's copy constructor.
If class U is derived from T, its clone function would look the same
except that it would return a U& or U*. That is possible now because
C++ now supports "covariant return types". C++ did not originally
have covariant return types, so a virtual clone function would have
had to return a pointer to the most-base class. In the presence of
multiple inheritance, that wouldn't always be possible.
class A { ... virtual A* clone(); };
class B { ... virtual B* clone(); };
class C : public A, public B { ??? };
Even without the multiple inheritance problem, returning a pointer
to the base class is not ideal.
Those reasons I think are why C++ did not have a clone operation:
If it were possible to do at all, it was easy for the programmer
to do it just as well as the compiler could do it.
Why wasn't clone added later? I don't remember anyone ever suggesting
it. It is still easy to write your own. In addition, I don't see
cloning as fundamental to C++, although it may be fundamental in
other languages. In particular, C++ does not use the heap unless
you ask for it explicitly, unlike other OO languages.
---
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 ]