Xref: utzoo comp.lang.c++:11703 comp.std.c++:604 Newsgroups: comp.lang.c++,comp.std.c++ Path: utzoo!utgpu!craig From: craig@gpu.utcs.utoronto.ca (Craig Hubley) Subject: allow convertible return types? (was Re: C++ typing not so strong) Message-ID: <1991Feb18.235340.21856@gpu.utcs.utoronto.ca> Followup-To: comp.std.c++ Organization: Craig Hubley & Associates References: <9102182022.AA05904@ucbvax.Berkeley.EDU> Date: Mon, 18 Feb 1991 23:53:40 GMT 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