Topic: standard 'clone' method? AND: const inheritance


Author: maxtal@Physics.usyd.edu.au (John Max Skaller)
Date: Sun, 26 Feb 1995 06:01:51 GMT
Raw View
In article <FENSTER.95Feb13232455@ground.cs.columbia.edu>,
Sam Fenster <fenster@ground.cs.columbia.edu> wrote:
>maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>> [Exactly what happens assigning classes with virtual bases and user defined
>> assignment operators is not well specified in the WP at the moment.]
>
>It's up to the user's operator=() code, no?  It unavoidably performs multiple
>assignment of the virtual base, right?


 If a class is assigned with a user coded operator=(),
nothing is unavoidable -- everything is specified by the user.

 There are two problems:

 1) compiler generated assignments CANT know what to do sometimes
  -- it is an ommission from the language

 2) user written assignments may suddenly find there is no way
 to do what they want to do because the base class design
 did not take the problem into account in advance.

In case (2) one can argue it is always a fault in the design
of the classes. In case (1) it is a fault in the C++ language
and should be fixed.

I would prefer making user definition of assignment easier by
extending the language to repair the ommission.

However, it only makes a semantic difference if you are doing stupid <grin>
things like deriving concrete classes from concrete classes.

Of course, even with pure abstractions, multiple assignments can
severely degrade performance. No self respecting compiler
given the complete responsibility for generating assignment
operators should ever do multiple assignments.

However, the access mode of assignments which defaults is wrong
for abstract classes, and so most of my abstractions now
have trivial protected user defined assignments. Since they're
inlined I _hope_ my compiler does the right thing.


--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,
        81A Glebe Point Rd, GLEBE   Mem: SA IT/9/22,SC22/WG21
        NSW 2037, AUSTRALIA     Phone: 61-2-566-2189




Author: fenster@ground.cs.columbia.edu (Sam Fenster)
Date: 14 Feb 1995 04:24:55 GMT
Raw View
maxtal@physics.su.OZ.AU (John Max Skaller) writes:
> [Exactly what happens assigning classes with virtual bases and user defined
> assignment operators is not well specified in the WP at the moment.]

It's up to the user's operator=() code, no?  It unavoidably performs multiple
assignment of the virtual base, right?




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Sat, 4 Feb 1995 18:58:40 GMT
Raw View
In article <russgold-0102952353040001@slip-19.netaxs.com> russgold@netaxs.com (Russell Gold) writes:
>My apologies if this has already been suggested, but I would like to see
>some kind of standard 'clone' function for all classes:

 I agree. I wrote up a proposal for exception handling
lattice in which the (Smalltalk like) root was :

 struct clonable {
  virtual clonable const* clone() const=0;
  virtual ~clonable(){}
 protected:
  virtual void operator=(clonable const&){}
 };

and then

 struct exception : public virtual clonable {
  exception const *clone()const=0;
 protected:
  virtual void operator=(exception const&){}
 };

but I was told it wouldn't fly so I dropped it. Too bad. I think
the need to clone objects is fundamental to object oriented
programming.

The clone() method uses a covariant return. The assignment
operator is deliberately implemented and protected
to prevent the following problem:

 struct concrete : public virtual clonable {
  int x;
  // default assignment is OK here
  concrete const* clone()const { return new(*this); }
  conrete(int xx) : x(xx) {}
 };

 concrete a(1), b(2);
 clonable &ra = a;
 ra = b; // access violation

instead of

 ra = b; // woops, fails to assign "2" to a.x

[Exactly what happens assigning classes with virtual bases
and user defined assignment operators is not well specified
in the WP at the moment.]

When a derived class is a generalisation (added states),
assignment of the base cannot work.
There is not way to "fetch" the added states from a base
reference on the RHS. So the covariant assignment of
the base should be inhibited. For example:

 class B {};
 struct D : B { int x; }; // add new states
 D l, r;
 B & b = l;
 b = r; // woops

The "r" is bound to the const B& parameter of the B assignment
operator, and via the B there is no knowledge of the "x" added.

For specialisation subtypes, assignment can't work
either, because a specialisation has _less_ states.
For example:

 class Matrix { };
 struct SymmetricMatrix : Matrix { .. }
 SymmetricMatrix s;
 Matrix m;
 s=m; // woops

Both these cases fail for the same reason: assignment is
covariant, and at least in C++ the only way to "dispatch"
on two types is statically. That is, it is safe to
do assignments from a lattice leaf to the same class, e.g.

 // specialisation
 SymmetricMatrix s1, s2; .. // "is a Matrix"
 s1=s2; // safe

 // generalisation
 ColouredPoint p1, p2; // "has a point"
 p1=p2; // safe

 // specialisation
 Square q1, s2; // "is a rectangle"
 q1=q2;

Once a covariant assignment is accessible, you should
not derive another class.

More generally, the result applies to mutators (with more
conditions).

BTW: I'm beginning think "const inheritance" might not
be such a bad idea after all. Because it is hard
to safely derive from a class with public mutators.

 struct D : public const B { .. }

makes the const methods of B public, and the non-const methods
of B private to D (accesible in D only).  For example:

 class Rectangle {  double w,h; ... public:
  setWidth(double); setHeight(double); };
 struct Square : public const Rectangle {
  void setSide(double d) { setwidth(d); setHeight(d); }
 };

This particular implementation may, or may not, work, depending
on the representation used. So a constant Square is a Rectangle,
and a mutable Square may, or mayn not, use the implementation
of a mutable Rectangle (private base).

Hence:

 Square *s;
 Rectangle *r=s; // error
 Rectangle const *cr = s; // OK

in public.

I think this solves the problem mentioned by someone that
const inheritance is too restrictive. If the mutators
remain accessible _in_ the derived class, then
they are inaccessible publically, but that can be fixed
by access modifiers or forwarding functions.

The alternative is to defined  an abstract lattice
in which _all_ the covariant mutators are protected. They are
made public in the concrete leaves, from which one
does not derive.
--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,
        81A Glebe Point Rd, GLEBE   Mem: SA IT/9/22,SC22/WG21
        NSW 2037, AUSTRALIA     Phone: 61-2-566-2189