[comp.lang.c++] use of overloaded operators with derived classes?

haskell@grant.cs.unc.edu (Phyllis Haskell) (01/13/88)

We're having a problem trying to use overloaded operators with derived classes.
A sample program follows in which we are attempting to pass a derived class to
the routine "test", which we want to multiply the instances of any derived 
class.  We want "test" to be a generic routine which does not need to "worry"
about the types of arguments used in its invocation.  Is there a way to do this?


#include <stdio.h>
class Datatype
{
  public:
    virtual Datatype* newobj() {fprintf (stderr,"newobj for base\n");}
    virtual void dump() { fprintf (stderr," dump undefined for base class\n");}
};

class Dint: public Datatype
{
  int temp;
  public:
    Dint () {}
    Dint (int in) {temp = in;}
    Dint& operator* (Dint& in) 
    { 
      Dint& newvar = *new Dint;
      newvar.temp = temp * in.temp;
      return newvar;
    }
    Datatype* newobj() {fprintf (stderr,"newobj for Dint\n");
			return new Dint;}
    void dump () { fprintf (stderr, "Dint = %d\n", temp); }
};

void test(Datatype& in, Datatype& in2)
{
  Datatype* temp  = in.newobj();   // create an instance of a subclass ??
/* 
   (*temp) = in * in2;      // here's our problem - how do we get proper 
                            // * operator for subclass. (commented to prevent
                            // compiler error)
*/
  temp->dump();
}

main ()
{
  Dint inta(5), intb(6);
  test(inta, intb);
}  

weh@druhi.ATT.COM (HopkinsBWE) (01/14/88)

In article <656@thorin.cs.unc.edu>, haskell@grant.cs.unc.edu (Phyllis Haskell) writes:
# 
# We're having a problem trying to use overloaded operators with derived classes.
# A sample program follows in which we are attempting to pass a derived class to
# the routine "test", which we want to multiply the instances of any derived 
# class.  We want "test" to be a generic routine which does not need to "worry"
# about the types of arguments used in its invocation.  Is there a way to do this?
# 
# 
# #include <stdio.h>
# class Datatype
# {
#   public:
#     virtual Datatype* newobj() {fprintf (stderr,"newobj for base\n");}
#     virtual void dump() { fprintf (stderr," dump undefined for base class\n");}
      virtual Datatype& operator * (Datatype& in) {}
      virtual int getTemp () {}
      virtual void operator = (Datatype&) {}
# };
# 
# class Dint: public Datatype
# {
#   int temp;
#   public:
#     Dint () {}
#     Dint (int in) {temp = in;}
/*****************************************************************************
#     Dint& operator* (Dint& in) 
#     { 
#       Dint& newvar = *new Dint;
#       newvar.temp = temp * in.temp;
#       return newvar;
#     }
****************************************************************************/
      Datatype& operator* (Datatype& in) 
      { 
        Dint& newvar = *new Dint;
        newvar.temp = temp * in.getTemp ();
	return newvar;
      }
      int getTemp () { return temp; }
      void operator = (Datatype& in) { temp = in.getTemp (); }
#     Datatype* newobj() {fprintf (stderr,"newobj for Dint\n");
# 			return new Dint;}
#     void dump () { fprintf (stderr, "Dint = %d\n", temp); }
# };
# 
# void test(Datatype& in, Datatype& in2)
# {
#   Datatype* temp  = in.newobj();   // create an instance of a subclass ??

#    (*temp) = in * in2;      // here's our problem - how do we get proper 
#                             // * operator for subclass. (commented to prevent
#                             // compiler error)

#   temp->dump();
# }
# 
# main ()
# {
#   Dint inta(5), intb(6);
#   test(inta, intb);
# }  

Compilation and execution:

weh<top>CC net.c
CC  net.c:
"net.c", line 10: warning: no value returned from Datatype::newobj()
"net.c", line 10: warning: no value returned from Datatype::operator *()
"net.c", line 10: warning: no value returned from Datatype::getTemp()
cc    net.c -lC
weh<top>a.out
newobj for Dint
Dint = 30


The problem was that operator * was not declared for Datatype and the
references to the Dint objects is via Datatype references. C++ requires
that any operation invoked on an object via a some reference or pointer
must be defined for the type of the reference or pointer. In other words,
the type of the reference or pointer defines the set of operators than
can be invoked (a.k.a. the ``signature''). Those operations whose
implementations are to be provided by derived objects would be declared
as virtual.

This example, as you can tell from the changes that had to be made,
presented a number of other complications, the reasons for which are
left as an exercise for the reader.

				Bill Hopkins
				AT&T
				ihnp4!druhi!weh

jima@hplsla.HP.COM (jim adcock ) (01/14/88)

/* 

   If I understand your test constraints correctly, this is as close as
   I can figure out to meeting your requirements.  The general idea is
   that in each derived class Dxxxx, derived from Datatype, there would
   be two operator* methods:

        virtual  Datatype& operator*(Datatype& in)        and
   [non-virtual] Dxxxx&    operator*(Dxxxx& in)

   where the second (non-virtual) method would provide a fast in-line
   method to be used between two objects of class Dxxxx for "real"
   programming usage, and the first method provides a "virtual" interface
   to the second method, to be called by the test routine.

   Unfortunately, allowing overloading of methods NOT defined in the base
   class does not seem to be implemented in the present AT&T compiler.
   (See what happens when you change operator_mpy to operator* in all
    places where operator_mpy is used -- the AT&T compiler says: "Sorry,
    we haven't implemented this feature !!!")

   So, my workaround was to come up with the "fake" name "operator_mpy"
   for the virtual version of the "operator*" function. *** SIGH ***
   It will be nice when we get a "finalized" C++ compiler !!!

   PS:

   The approach taken puts a "hidden" pointer to the virtual function dispatch
   table in each object.  It's unclear from your question whether this would
   be acceptible overhead or not.  If not, the only solution I would have is
   to put the virtual/test functions in each class inside an #ifdef TEST
   block, so that they would not be compiled in the final release of your software.
   (The pointer to the virtual dispatch table is placed in an object ONLY if
    it or its superclasses uses virtual functions)

*/

#include <stdio.h>
class Datatype
{
  public:
    Datatype() {};
    virtual const char* isA() {return "Datatype";};
    virtual void dump() { fprintf (stderr,"no value to dump in Datatype\n");};
    virtual Datatype& /* operator* */ operator_mpy(Datatype& in) {
      fprintf(stderr,"Datatype operator* incorrectly used\n"); 
      exit(1); return *this;
    };
};

class Dint: public Datatype
{
  int temp;
  public:
    Dint () {};
    Dint (int in) {temp = in;};
    virtual const char* isA() { return "Dint";};
    virtual Datatype& /* operator* */ operator_mpy(Datatype& in);
    Dint& operator* (Dint& in) 
    {
        Dint& newvar = *(new Dint);
        newvar.temp = temp * in.temp;
        return newvar;
    }
    void dump () { fprintf (stderr, "Dint = %d\n", temp); }
};

virtual Datatype& /* operator* */ Dint::operator_mpy(Datatype& in) 
{ 
  if (in.isA() != isA()) 
  {
      fprintf(stderr,"mismatch in Dint operator*\n");
      exit(1);
  }
  return (*this) * (*(Dint*)(&in));
}

class Dflt: public Datatype
{
  float temp;
  public:
    Dflt () {};
    Dflt (int in) {temp = (float)in;};
    virtual const char* isA() { return "Dflt";};
    virtual Datatype& /* operator* */ operator_mpy(Datatype& in);
    Dflt& operator* (Dflt& in) 
    {
        Dflt& newvar = *(new Dflt);
        newvar.temp = temp * in.temp;
        return newvar;
    }
    void dump () { fprintf (stderr, "Dflt = %f\n", temp); }
};

virtual Datatype& /* operator* */Dflt::operator_mpy(Datatype& in) 
{ 
  if (in.isA() != isA()) 
  {
      fprintf(stderr,"mismatch in Dflt operator*\n");
      exit(1);
  }
  return (*this) * (*(Dflt*)(&in));
}

void test(Datatype& in, Datatype& in2)
{
    Datatype* temp  = &(in.operator_mpy(in2));
    temp->dump();
}

main ()
{
  Dint inta(5), intb(6);
  Dflt flta(5), fltb(6);
  test(inta, intb);
  test(flta, fltb);
}