Topic: Proposal for a new type of inheritance


Author: Steve Heller <steve@steveheller.com>
Date: Thu, 16 May 2002 19:07:45 GMT
Raw View
This replaces my previous proposal for a "forward" declaration to
allow forwarding of all functions to a contained object.

Francis Glassborow <francis.glassborow@ntlworld.com> wrote:

>In article <b0q0eu80d261qvrr5qkch0snr577m1eofi@4ax.com>, Steve Heller
><steve@steveheller.com> writes
>>>At the very least, your proposal would force the placement
>>>of the base part of a derived object at zero offset, which
>>>is very likely unacceptable.
>>
>>  Ok, then restrict it to the case where the derived class doesn't
>>define its own new and delete operators or multiple inheritance.
>
>As you add restrictions the potential utility decreases and the problem
>of teaching people to code safely goes up. The real problem from my
>perspective is the use of inheritance to tweak an existing concrete
>class. Certainly it would be useful to be able to re-use an existing
>class with another name but this needs to be considered as the problem.
>IOWs state what problem you are trying to solve and then consider viable
>solutions to it. I think that a work item I have from the evolution work
>group of WG21 concerning inheriting constructors may throw up more
>general solutions to the problem of reusing a (possible tweaked)
>concrete class.

  What I would actually like to do is to forward all functions (not
otherwise defined) called on objects of my class to an object of
another class, without having to forward every such function
explicitly, and while maintaining object semantics (i.e, not
overloading ->). The only way I've figured out to do this is by
inheriting publicly from the class to which I want to forward, but I
realize that solution has its own drawbacks. I'm sure others must have
run into this problem, and would also like to see a solution.
  My proposal is to add a new type of inheritance intermediate between
public and private, called "using" inheritance. This type of
inheritance would be like public inheritance in that it would allow
public references to all functions (except those overridden in this
class) in the base class part, and like private inheritance in that it
would not allow substitution of a derived class object for a base
class object. Here is an example:
//////////////////////////
class A
{
  A();
  FirstFunction();
  SecondFunction();
};

class B : using A
{
B();
SomeFunction();
FirstFunction();
};

int main()
{
  B b;
  b.SomeFunction(); // invokes B.SomeFunction();
  b.FirstFunction(); // invokes B.FirstFunction();
  b.SecondFunction(); // invokes A.SecondFunction();
  A a = b; // illegal unless B has a conversion to an A
  B b1 = A(); // illegal unless B has a constructor that takes an A
  A* a1 = &b; // illegal, as with private inheritance
}
//////////////////////////

This would allow B to leverage A's functionality in a safe and
convenient manner, without the problems associated with allowing a B
to be treated as an A when that would be inappropriate (e.g., when A
does not have a virtual destructor).

--
Steve Heller
http://www.steveheller.com
Author of "Learning to Program in C++", "Who's Afraid of C++?", "Who's Afraid of More C++?",
"Optimizing C++", and other books
Free online versions of "Who's Afraid of C++?" and "Optimizing C++" are now available
at http://www.steveheller.com/whos and http://www.steveheller.com/opt

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Francis Glassborow <francis.glassborow@ntlworld.com>
Date: Thu, 16 May 2002 21:00:47 GMT
Raw View
In article <as37euctkga0eks3h1tijhcoa1pakj5rdv@4ax.com>, Steve Heller
<steve@steveheller.com> writes
>This would allow B to leverage A's functionality in a safe and
>convenient manner, without the problems associated with allowing a B
>to be treated as an A when that would be inappropriate (e.g., when A
>does not have a virtual destructor).

This is one of several ideas that I have seen for tackling this problem.
I will add it to the list of possible syntactical attacks on the wider
semantic problem.

--
Francis Glassborow      ACCU
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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Steve Heller <steve@steveheller.com>
Date: Fri, 17 May 2002 16:00:37 GMT
Raw View
Francis Glassborow <francis.glassborow@ntlworld.com> wrote:

>In article <as37euctkga0eks3h1tijhcoa1pakj5rdv@4ax.com>, Steve Heller
><steve@steveheller.com> writes
>>This would allow B to leverage A's functionality in a safe and
>>convenient manner, without the problems associated with allowing a B
>>to be treated as an A when that would be inappropriate (e.g., when A
>>does not have a virtual destructor).
>
>This is one of several ideas that I have seen for tackling this problem.
>I will add it to the list of possible syntactical attacks on the wider
>semantic problem.

  Thank you.

--
Steve Heller
http://www.steveheller.com
Author of "Learning to Program in C++", "Who's Afraid of C++?", "Who's Afraid of More C++?",
"Optimizing C++", and other books
Free online versions of "Who's Afraid of C++?" and "Optimizing C++" are now available
at http://www.steveheller.com/whos and http://www.steveheller.com/opt

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: schnitker@sigma-c.com (Uwe Schnitker)
Date: Fri, 17 May 2002 17:01:51 GMT
Raw View
Steve Heller <steve@steveheller.com> wrote in message news:<as37euctkga0eks3h1tijhcoa1pakj5rdv@4ax.com>...
> This replaces my previous proposal for a "forward" declaration to
> allow forwarding of all functions to a contained object.
>

<SNIP>

>   What I would actually like to do is to forward all functions (not
> otherwise defined) called on objects of my class to an object of
> another class, without having to forward every such function
> explicitly, and while maintaining object semantics (i.e, not
> overloading ->). The only way I've figured out to do this is by
> inheriting publicly from the class to which I want to forward, but I
> realize that solution has its own drawbacks. I'm sure others must have
> run into this problem, and would also like to see a solution.
>   My proposal is to add a new type of inheritance intermediate between
> public and private, called "using" inheritance. This type of
> inheritance would be like public inheritance in that it would allow
> public references to all functions (except those overridden in this
> class) in the base class part, and like private inheritance in that it
> would not allow substitution of a derived class object for a base
> class object.

Some comments:

1) I like the general concept. It will become a lot easier to convince people
   to avoid (mis)using public inheritance for convenience even if it violates
   the LSP if this avoidance doesn't cost to many keystrokes.

   However, it should be noted that this form of inheritance would solve the
   conceptual and semantic problem with public inheritance over/mis -use, but
   the physical dependency would remain.

   People would have to be careful to switch to "using inheritance" only if
   they (their code) can stand the dependency, and to continue using
   handle/pimpl/whatever .. if they need decoupling.

2) It would be nice if other creative (ab)uses of public inheritance could
   also be handled by this idea, e.g. inheriting from traits classes to
   make typenames visible.

3) I wouldn't interpret this "using inheritance" as lying in between
   public and private - or, more precicely, protected - inheritance.
   It is, technically, but its usage would be quite different to both.

   An interesting question is: How would the intended, the afforded and
   the possible use of this feature relate?

   Forwarding to a concrete class (without virtual functions) looks
   straightforward (Nice pun, isn't it?), because it is essentially
   equivalent with the writing-forwarding-functions-to-a-contained-class
   way.

   "Using inheriting" from a class designed for inheritance - perhaps
   because you want to violate LSP - is different, and could easily
   lead to mixing "use of the class as client, via forwarding" with
   "implementing variations of the class, via virtual functions". If
   the base class is abstract, you would be forced to do so, because
   you must implement the pure virtuals.

4) Couldn't we have a "using membership" instead? Then one could write
     class X {};
     class Y {
        using X x_;
     };
   instead of
     class Y : using Y {};

   This would the "dual nature" problem mentioned above, because you
   couldn't override virtuals. (Here I spot a problem: If it is not
   explicitly forbidden, you could override a public virtual function
   of Y, and if it is called from within Y also, then Y would call its
   original version, because there is no virtual dispatch, but X or its
   clients would call the overridden version. Very bad.)

5) One problem with the proposal is IMHO that it breaks orthogonality.
   The access protection possibilities for inheritance would be different
   from those for class members.

   A strange, but interesting idea would be to make this "use of using"
   orthogonal to the access level, leading to

     class Y : public using X {};

   and making it possible to write

     class Y : protected using virtual X {};

   which elegantly compensates lack of usefullness with eloquence and charm.

6) One last enhancement to the proposal:

   Isn't this an opportunity to make up for the loss of one, now deprecated
   usage of the "static" keyword? We could have:

     class Y : static public X {};

   It will take a little reasoning to explain how the word "static" has an
   implied meaning which describes a "forwarding" concept, but given the
   outstanding creativity of the C++ community as a whole this should't
   stop us.

   After all, the number of different uses of "static" in C++ has been used
   by some people to track the progress of its evolution (and, of course, to
   "prove" that its better than C).

Just my 2 cents,

Uwe

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: Helmut Zeisel <helmut.zeisel@vai.at>
Date: Fri, 17 May 2002 17:41:11 GMT
Raw View
Steve Heller wrote:

>   What I would actually like to do is to forward all functions (not
> otherwise defined) called on objects of my class to an object of
> another class, without having to forward every such function
> explicitly, and while maintaining object semantics (i.e, not
> overloading ->).

As I understand, what you want to do could "almost" be done by templates.
As a solution to your problem, I would rather prefer to see templates extend
in such a way that your problem can be completely solved using templates.

See my code below as a scetch how this might be done

Helmut

============ Code example

#include <iostream>

class A
{
public:
  void print(int i) {std::cout << i << std::endl;}
  int  add(int i, int j) {return i+j;}
};

template<typename Base>
class Forwarder
{
public:

  template<typename Return, typename Arg>
  Return operator()(Return (Base::*f)(Arg),Arg arg)
  {
    return (mBase.*f)(arg);
  }

 // Problem 1: In current C++, I have to repeat the same code for every possible number of
arguments;
 // maybe some syntax for "parameterlist" might be helpful.
  template<typename Return, typename Arg1, typename Arg2>
  Return operator()(Return (Base::*f)(Arg1, Arg2),Arg1 arg1, Arg2 arg2)
  {
     return (mBase.*f)(arg1, arg2);
  }
 // Repeat for 3,4,5,... tempate arguments ;-)
private:
  Base mBase;
};


int main()
{
  Forwarder<A> f;
  f(&A::print,5);                                          // Problem 2: f.print(5) would be
much nicer
  std::cout << f(&A::add,4,7) << std::endl;  //                   f.add(4,7) would be much nicer

  return 0;
}

================ End code example


---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]