Topic: allow convertible return types? (was C++ typing not so strong)
Author: craig@gpu.utcs.utoronto.ca (Craig Hubley)
Date: 18 Feb 91 23:53:40 GMT Raw View
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