stuarth@csis.dit.csiro.au (Stuart Hungerford) (03/20/91)
I'd like to be able to make overloaded operator functions virtual. Say I had this: class Base { public: virtual int operator== (const Base& B); //... }; then I'd like to override this virtual function in a derived class of Base. BUT the language requires that overriding virtual functions requires an exact match so I have to say: class Derived : public Base { public: int operator== (const Base& B); //... }; which means I'll probably end up casting Base classes to Derived ones in the implementation of Derived::operator==. I guess the same problem will hold with any function that takes class references or pointers as arguments. Am I missing something important here? How do you handle this kind of situation? Any advice much appreciated. Stuart +---------------------------------------------------------------------------+ | Internet : stuarth@csis.dit.csiro.au | | | Voice : +61-6-2750941 | _--_|\ | | Fax : +61-6-2571052 | / \ | | Postal : CSIRO Division of Information Technology, | \_.--._/ | | GPO Box 664, Canberra ACT 2601 | v | | AUSTRALIA | | +---------------------------------------------------------------------------+
mittle@blinn.watson.ibm.com (Josh Mittleman) (03/22/91)
The problem you describe is precisely the same one addressed earliest this week under the subject "Seeking neat way to do binary "virtual" functions. A followup post by cok@islsun.kodak.com suggests some ways around the problem, but also ntoes that a more fundmanetal solution is really needed. I've run across the same problem in another context, and I have what might be a reasonable solution. I would appreciate comments. What we need is a new kind of template or prototype for a class, so that the compiler can be told to treat certain class names as parameters. To work with the most recent example: class Base { public: virtual int operator==(const Base&); }; class Derived : public Base { public: int operator==(const Derived&); }; We would really like Derived::operator== to match the virtual Base::operator==, but we need to be able to treat the argument as a Derived&. My solution is somehow to tell the compiler that Base& is to be treated as a parameter of the parameterized class Base in certain circumstances. A possible syntax: class Base(parameter Base) // Defines formal parameters { public: virtual int operator==(parameter const Base&); // parameterized virtual // function. }; This seems to solve the problems suggested, and makes a great deal of sense to me. It requires the designer of class Base to forsee which members will need to be parameterized; I'm not sure if that is a good thing or not. Comments? =========================================================================== Josh Mittleman (mittle@ibm.com or joshua@paul.rutgers.edu) J2-C28 T.J. Watson Research Center, PO Box 704, Yorktown Heights, NY 10598
wmm@world.std.com (William M Miller) (03/22/91)
mittle@blinn.watson.ibm.com (Josh Mittleman) writes: > class Base > { > public: > virtual int operator==(const Base&); > }; > > class Derived : public Base > { > public: > int operator==(const Derived&); > }; > > We would really like Derived::operator== to match the virtual > Base::operator==, but we need to be able to treat the argument as a > Derived&. No, you really *don't* want that. Allowing something like that would open a major hole in the type system. Assuming Derived::operator==() actually did override Base::operator==(), code like the following would be possible: Base b; Derived d; Base& br = d; if (br == b) ... Since "br" actually refers to a Derived object, the "==" invokes Derived::operator==() -- but it passes a Base object as the argument instead of the Derived object expected by Derived::operator==(). Boom! This is a frequent request, but it just doesn't make sense when you look at the ramifications. -- William M. Miller, Glockenspiel, Ltd. wmm@world.std.com
mittle@blinn.watson.ibm.com (Josh Mittleman) (03/23/91)
I wrote: > class Base > { > public: > virtual int operator==(const Base&); > }; > > > class Derived : public Base > { > public: > int operator==(const Derived&); > }; > > > We would really like Derived::operator== to match the virtual > Base::operator==, but we need to be able to treat the argument as a > Derived&. In article <1991Mar22.044801.26365@world.std.com> William M Miller writes: > No, you really *don't* want that. Allowing something like that would open a > major hole in the type system. Assuming Derived::operator==() actually did > override Base::operator==(), code like the following would be possible: > > Base b; > Derived d; > Base& br = d; > > if (br == b) ... > > Since "br" actually refers to a Derived object, the "==" invokes > Derived::operator==() -- but it passes a Base object as the argument instead > of the Derived object expected by Derived::operator==(). Boom! I don't see that this follows. I am suggesting extension to the way virtual invocation works. In your example, you are explicitly asking for Derived::operator==(Base&). At compile time, we accept this because it will always match something, i.e., Base::operator==(Base&). At run time, we check to see what the Base& really points to. If it happened to be a Base, then we would invoke Base::operator==(Base&). If it happens to be a Derived, we invoke Derived::operator==(Derived&). The programmer could also provide a member Derived::operator==(Base&), if he wanted to handle that case in a special way. To summarize: Derived d, e; Base b, c; Base &br1 = d, &br2 = e, &br3 = b, &br4 = c; Derived::operator==(Derived&) invoked by (d == e), (br1 == br2), (br1 == e), (d = br2) Derived::operator==(Base&) invoked by (d == b), (br1 == b), (d = br3), (br1 = br3) Base::operator==(Base&) invoked by (b == c), (b == d), (b == br1), (b = br3), (br3 = c), (br3 = d), (br3 = br1), (br3 = br4) I may be missing something, but I see neither ambiguity nor danger in this system.
wmm@world.std.com (William M Miller) (03/23/91)
mittle@blinn.watson.ibm.com (Josh Mittleman) writes: > To summarize: > > Derived d, e; > Base b, c; > Base &br1 = d, &br2 = e, &br3 = b, &br4 = c; > > Derived::operator==(Derived&) invoked by (d == e), (br1 == br2), > (br1 == e), (d = br2) > > Derived::operator==(Base&) invoked by (d == b), (br1 == b), (d = br3), > (br1 = br3) > > Base::operator==(Base&) invoked by (b == c), (b == d), (b == br1), > (b = br3), (br3 = c), (br3 = d), > (br3 = br1), (br3 = br4) > > I may be missing something, but I see neither ambiguity nor danger in this > system. Sorry, this is a much more ambitious modification to the existing virtual mechanism than I had realized. When you originally said, > class Base > { > public: > virtual int operator==(const Base&); > }; > > class Derived : public Base > { > public: > int operator==(const Derived&); > }; > > We would really like Derived::operator== to match the virtual > Base::operator==, but we need to be able to treat the argument as a > Derived&. I had understood that to mean that all you wanted was for Derived::operator==(const Derived&) to override Base::operator==(const Base&) and nothing more. That proposal, which I have seen made by others, leads directly to the possibility of passing a Base object as the argument to Derived::operator==(const Derived&), as I pointed out. What you are asking for, though, as I understand it, is that virtual dispatch be based not only on the type of the object for which the member function is invoked but also on the type(s) of the argument(s). This looks awfully hard to me unless you go to a full-blown dynamic type resolution scheme like Objective-C or Smalltalk -- or are you suggesting limiting it only to 2-level matching (i.e., 1 argument), with the argument required to be a pointer or reference to the base class first declaring the virtual function? If so, it's pretty straightforward to figure out how to do that with double tables and one extra level of indirection, but it seems like a rather arbitrary restriction. Anyhow, this is a new approach to the issue that I hadn't seen before, and my earlier reaction was based on insufficient information. I'd be interested in seeing a more complete writeup on it detailing exactly what you're proposing (number and type of arguments on which virtual dispatch could be based, possible implementation strategies, impact on compatibility with existing code, utility, etc.). Also, would you be advocating applying the proposal to non-member overloaded operators? If you can do virtual dispatch not just on object type but on operand type as well, it would seem natural to want "virtual" non-member operators, too. -- William M. Miller, Glockenspiel, Ltd. wmm@world.std.com