[comp.lang.c++] How smart is operator resolution?

jamesh@cs.umr.edu (James Hartley) (11/28/90)

I recently tried taking advantage of virtual functions by creating a generic
routine which would take a base class pointer and output the appropriate 
derived class where the insertor was properly overloaded.  The following 
program attempts to illustrate the idea.  Turbo C++ balks at the output
statement in main() because the insertor is not defined with respect to
the base class.  Is this operator resolution beyond the restraints of the
language, or is this a bug in the compiler?

-----------------------  BEGIN CODE -------------------------------------

#include <iostream.h>

const float pi = 3.14159;

class figure {			// abstract base class definition
protected:
    float r, h;
public:
    virtual float volume(void) = 0;
};

class sphere : public figure {	// derived class definition
public:
    sphere(float radius)  { r = radius; }
    virtual float volume(void)  { return 4.0 / 3.0 * pi * r * r * r; }
    friend ostream &operator<<(ostream&, sphere&);
};

ostream &operator<<(ostream &strm, sphere &sph) {
    strm << "VOLUME OF SPHERE:\n";
    strm << "radius = " << sph.r << "\n";
    strm << "volume = " << sph.volume() << "\n";
    return strm;
}

main() {
    figure *fig = new sphere(2.0);
    cout << *fig << "\n";
    return 0;
}

-------------------------  END CODE -------------------------------------

-- 
James J. Hartley                      Internet: jamesh@cs.umr.edu
Department of Computer Science          Bitnet: jamesh@cs.umr.edu@umrvmb.bitnet
University of Missouri - Rolla            UUCP: ...!uunet!cs.umr.edu!jamesh

fuchs@it.uka.de (Harald Fuchs) (11/30/90)

jamesh@cs.umr.edu (James Hartley) writes:

>I recently tried taking advantage of virtual functions by creating a generic
>routine which would take a base class pointer and output the appropriate 
>derived class where the insertor was properly overloaded.  The following 
>program attempts to illustrate the idea.  Turbo C++ balks at the output
>statement in main() because the insertor is not defined with respect to
>the base class.  Is this operator resolution beyond the restraints of the
>language, or is this a bug in the compiler?

You get the inserter you want by making it virtual. You can't do this
directly because the standard insertion operator<< expects your
"figure" to be the 2nd argument. Do it like that:

class figure {                  // abstract base class definition
protected:
    float r, h;
public:
    virtual float volume(void) = 0;
    virtual void printOn (ostream&) const = 0;
};

inline ostream& operator<< (ostream& s, const figure& x) {
  x.printOn (s);
  return s;
}

class sphere : public figure {  // derived class definition
public:
    sphere(float radius)  { r = radius; }
    virtual float volume(void)  { return 4.0 / 3.0 * pi * r * r * r; }
    void printOn (ostream&) const;
};

void sphere::printOn (ostream& strm) const {
    strm << "VOLUME OF SPHERE:\n";
    strm << "radius = " << sph.r << "\n";
    strm << "volume = " << sph.volume() << "\n";
}

main() {
    figure *fig = new sphere (2.0);
    cout << *fig << "\n";
    return 0;
}
--

Harald Fuchs <fuchs@it.uka.de> <fuchs%it.uka.de@relay.cs.net> ...
<fuchs@telematik.informatik.uni-karlsruhe.dbp.de>   *gulp*

rmartin@clear.com (Bob Martin) (11/30/90)

In article <1747@umriscc.isc.umr.edu> jamesh@cs.umr.edu (James Hartley) writes:
>I recently tried taking advantage of virtual functions by creating a generic
>routine which would take a base class pointer and output the appropriate 
>derived class where the insertor was properly overloaded.  The following 
>program attempts to illustrate the idea.  Turbo C++ balks at the output
>statement in main() because the insertor is not defined with respect to
>the base class.  Is this operator resolution beyond the restraints of the
>language, or is this a bug in the compiler?
>
>-----------------------  BEGIN CODE -------------------------------------
>
>#include <iostream.h>
>
>const float pi = 3.14159;
>
>class figure {			// abstract base class definition
>protected:
>    float r, h;
>public:
>    virtual float volume(void) = 0;
>};
>
>class sphere : public figure {	// derived class definition
>public:
>    sphere(float radius)  { r = radius; }
>    virtual float volume(void)  { return 4.0 / 3.0 * pi * r * r * r; }
>    friend ostream &operator<<(ostream&, sphere&);
>};
>
>ostream &operator<<(ostream &strm, sphere &sph) {
>    strm << "VOLUME OF SPHERE:\n";
>    strm << "radius = " << sph.r << "\n";
>    strm << "volume = " << sph.volume() << "\n";
>    return strm;
>}
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This function defines the << operator for a sphere but not for a figure.
>
>main() {
>    figure *fig = new sphere(2.0);
>    cout << *fig << "\n";
^^^^^^^^^^^^^^^^^^^^^^^^^^
The compiler cannot resolve this statement because there is not << operator
defined for a figure.
>    return 0;
>}
>
>-------------------------  END CODE -------------------------------------
>
>-- 
>James J. Hartley                      Internet: jamesh@cs.umr.edu
>Department of Computer Science          Bitnet: jamesh@cs.umr.edu@umrvmb.bitnet
>University of Missouri - Rolla            UUCP: ...!uunet!cs.umr.edu!jamesh

What you need to do is declare a virtual function (say printOn) for sphere.
Then in operator<<(ostream&, figure&) call the virtual function.



-- 
+-Robert C. Martin-----+:RRR:::CCC:M:::::M:| Nobody is responsible for |
| rmartin@clear.com    |:R::R:C::::M:M:M:M:| my words but me.  I want  |
| uunet!clrcom!rmartin |:RRR::C::::M::M::M:| all the credit, and all   |
+----------------------+:R::R::CCC:M:::::M:| the blame.  So there.     |

jimad@microsoft.UUCP (Jim ADCOCK) (12/06/90)

Issues of overloading are probably too complicated to do justice here.
I refer you to pages 307 to 339 of "The Annotated C++ Reference Manual."

Consider instead the approach below -- the standard trick of introducing
an intermediate helper function to allow polymorphism on a second parameter.

const float pi = 3.14159;

class figure {            // abstract base class definition
protected:
    float r, h;
public:
    virtual float volume(void) = 0;
    friend ostream& operator<< (ostream& s, figure& self) 
        { return self.shiftout(s); }
    virtual ostream& shiftout(ostream&) = 0;
};

class sphere : public figure {    // derived class definition
public:
    sphere(float radius)  { r = radius; }
    virtual float volume(void)  { return 4.0 / 3.0 * pi * r * r * r; }
    virtual ostream& shiftout(ostream&);
};

ostream& sphere::shiftout (ostream &strm) {
    strm << "VOLUME OF SPHERE:\n";
    strm << "radius = " << r << "\n";
    strm << "volume = " << volume() << "\n";
    return strm;
}

main() {
    figure *fig = new sphere(2.0);
    cout << *fig << "\n";
    return 0;
}