[comp.lang.c++] allow convertible return types?

craig@gpu.utcs.utoronto.ca (Craig Hubley) (02/19/91)

In article <9102182022.AA05904@ucbvax.Berkeley.EDU> schweitz@lexvmc.iinus1.ibm.com ("Eric Schweitz") writes:
>| But we also want some "virtualness" here.
>
>Perhaps you could tell us why you `needed' these member functions to be
>virtual.

In order to pass DoubleImage* generically as Image* to a function
which calls the negation generically, without caring exactly which 
type it is.  Like any other virtual.  Relying on overloading restricts
one to compile-time resolution, which is why both the original poster,
and I, have been arguing for a more unified set of polymorphism rules
(followups to comp.std.c++ because this requires changes in the language).

In my opinion, abstract notions of "strong typing" don't apply clearly since
C++ supports a number of builtin conversions.  These types are in practice
treated "the same" because they are guaranteed convertible, and although
C++ will not apply more than one user-defined conversion it is free enough
with its builtins that usually programmers can treat int/short/char, etc.
as effectively one type.  Similarly for pointers to derived classes that are
used where pointers to bases are expected.

>| Example A:
>|
>| class Image {
>|     public:
>|         virtual const Image& operator-() const = 0;
>| };
>|
>| class DoubleImage: public Image {
>|     public:
>|         const DoubleImage& operator-() const;
>| };
>|
>| BUT THIS IS ILLEGAL because the two operator- declarations do not return the
>| same type.
>
>You're right here. However, if ``operator-'' had been declared as a
>non-`pure virtual' member function, you would have simple overloading of
>the unary ``-'' operator.

Yes, and the overloading solution would have worked fine if every scope
that was going to apply the unary- was going to be passed the DoubleImage
as a DoubleImage and not as an Image.  This may well be so in the present
application, and overloading is certainly very useful in overcoming the
inadequacies of C++ in building true type families, but it's far from an
optimal solution.

>| Question 1: Does anyone have a better suggestion?
>
>Yeah, don't make the operator- a pure virtual function if you don't have to.

If the original poster would show us some of the code *using* these features,
it would be easier to decide if he "had to".  That would still leave the
question of whether he had to 

>| Question 2: What is the rationale for requiring that the implementations of
>|         virtual functions in derived classes return exactly the same type as is
>|         declared in the base class?
>
>The rationale behind this is that it would make the following possible :

You mean, 

>   //....
>   Image i, *ip;
>   DoubleImage *dp;
>
>   ip = &i;
>   dp = func ((DoubleImage*) ip);

Note the cast to DoubleImage*...

>   //....
>where, func () is:
>
>   //....
>   DoubleImage* func (DoubleImage* p)
>   {
>      return p->operator-();
>   }
>   //....
>
>hence, func() would return a Image* to dp which is expecting a DoubleImage*.

But you have created this problem by casting ip, an Image*, to a DoubleImage*
yourself in this example.  That is always a risky maneuver, precisely because
your own or other functions may expect functionality of the Image* that it
doesn't have.  Forcing it to have the "right" return value doesn't solve the
problem, it only hides it.  If you have converted Image* to DoubleImage*
yourself, to create this problem, why can't the system do the same, to solve
it ?  After all, you have indicated that it is a reasonable conversion
yourself.

If you are uncomfortable with the compiler doing it, do it yourself...

>   dp = (DoubleImage*) func ((DoubleImage*) ip);

...but are there any negative consequences of letting the compiler assume
it can automatically use the same conversion on the same line as the 
programmer ?  C++ already assumes that a constructor with one argument
is a legitimate conversion between user-defined types, or from builtins to
user-defined types.  In other words, if you want to use such a conversion
for yourself you must also let the compiler use it.  So what is the
difference, if any, from this situation ?  Isn't C++ being inconsistent ?

>Schweitz.   schweitz@lexvmc.iinus1.ibm.com


-- 
  Craig Hubley   "...get rid of a man as soon as he thinks himself an expert."
  Craig Hubley & Associates------------------------------------Henry Ford Sr.
  craig@gpu.utcs.Utoronto.CA   UUNET!utai!utgpu!craig   craig@utorgpu.BITNET
  craig@gpu.utcs.toronto.EDU   {allegra,bnr-vpa,decvax}!utcsri!utgpu!craig