ngo@tammy.harvard.edu (Tom Ngo) (03/28/91)
Here's a wild idea whose purpose is to permit typesafe downcasts under
certain circumstances. Do others think it's worth pursuing? Can it
be generalized?
The problem:
class B {
virtual void foo(B*);
};
class D : public B {
virtual void foo(D*);
};
void bar(B* b1, B* b2) {
b1.foo(b2);
}
D d1;
D d2;
bar(&d1,&d2); // I want D::foo to be invoked
Currently, D::foo does not override B::foo. A way to cause this
to happen is for D::foo to be declared D::foo(B*) and downcast B*
to D* within the body of D::foo. This downcast is considered
unsafe; see ARM p. 210-211.
I call foo() a "binary" virtual function, meaning that the
intention is for it to operate on two objects of identical type,
even if the call is made with references or pointers to a base
class of the type.
I feel there is a strong need for such functions, and that
currently there is no way to implement them that is both typesafe
and elegant. For example, if you have a container class designed
to take B*'s and you have given it all D*'s, the container class
ought to be able to do things like compare objects in a D-specific
manner, etc. Right now if you want to implement this sort of
thing in a typesafe manner you have to make two separate virtual
function calls, one for each operand of the comparison, or some
such thing.
The proposed solution:
It has been argued that to implement typesafe downcasts would
require that type information be added to each object. I suggest
that objects already contain type information in their virtual
function tables. So here's the idea:
1. Relax the requirement that two functions' arguments match
EXACTLY for one to override the other. Instead, permit
D::foo(D*) to match B::foo(B*).
(In general, any argument of type D* or D* in the argument
list of D::foo would match an argument of type B* or B* in
B::foo.)
2. In that case, in D::foo(D* that) the compiler should
automatically generate code that ensures:
this->foo == that->foo
(You know what I mean.) If the equality fails, then perhaps
an addressing exception should be generated.
This solution generalizes trivially to cases in which D::foo has
several D* arguments.
Comments?
--Tom
--
Tom Ngo
ngo@harvard.harvard.edu
617/495-1768 lab number, leave messagechip@tct.com (Chip Salzenberg) (04/01/91)
According to ngo@tammy.harvard.edu (Tom Ngo): >The problem: > > class B { > virtual void foo(B*); > }; > class D : public B { > virtual void foo(D*); > }; > > void bar(B* b1, B* b2) { > b1.foo(b2); > } > > D d1; > D d2; > bar(&d1,&d2); // I want D::foo to be invoked To suppose that D::foo() should override B::foo() is to misunderstand the meaning of virtual functions, namely: A virtual function is _one_ function with potentially many implementations. The code quoted above shows that D::foo() is prepared to work on a D, while B::foo() is prepared only for a B. Thus they are not the same function. They may be equivalent in one way or another; they may even have the same body. But they are not the _same_ function. So the use of "virtual" for these two functions is a design error. -- Chip Salzenberg at Teltronics/TCT <chip@tct.com>, <uunet!pdn!tct!chip> "All this is conjecture of course, since I *only* post in the nude. Nothing comes between me and my t.b. Nothing." -- Bill Coderre