On Fri, 19 Mar 2010 09:28:21 -0400, Justin Johansson <[email protected]> wrote:

Maybe I don't understand your problem exactly, but in answer to
what you said here ...

Because of covariance, this works
awesome because you always get back the type you are using.

.. If you know in the first place (presumably by static analysis) the type you
are using then you know the type you expect to be returned, so why do
you need to even use an interface and expect some magic covariance to
be of use to you.

Because people may want to use the interface vs the actual class. There is no point of an interface if people don't use it to abstract the implementation. I want the interface to call the same function as the class, but the return type should be covariant. That is, someone who is using the derived type and wants to chain calls on such functions should not be forced to use only the interface supported calls after they make the first one.

Like I say, perhaps I misunderstand your point but feel from my own
endeavours that covariant return types are rarely useful and only serve
to push more entries into a virtual function table further down an inheritance
tree for no purposeful gain in the end.

covariant functions occupy one slot of the vtable. You may be misunderstanding how covariance works. See below.


Perhaps you could elaborate on what the real problem is that you want to solve.

Please accept my apologies if your question/proposal is obvious to all and sundry; just that personally I cannot reconcile this in my own mind that there is an end
to be met here by your means.

No apologies needed :)  I'll explain what I mean:

Covariance works like this:

class C
{
   C doSomething() {...; return this;}
}

class D : C
{
   D doSomething() {...; return this;}
   void doSomethingElse() {...}
}

If I have a D, I can call d.doSomething().doSomethingElse(), whereas if I have a C reference to a D, I cannot call the doSomethingElse part, but both c.doSomething() and d.doSomething() invoke the exact same function (D's version).

If you look at D's virtual table, it only has 2 entries, because the covariant version overrides the base version. In C++ this is not possible, because it does not consider that you can return a pointer to something that is both a C and a D at the same time. The benefit is one implementation for both interfaces (C or D).

However, if you have simple "wrapper" functions, like the ones I've outlined, in order to achieve covariance on those wrappers, you must override them.

e.g.:

class C
{
   C doSomething(ref int x) {...; return this;}
   C doSomething() {int dummy; return doSomething(dummy);}
}

class D : C
{
   D doSomething(ref int x) {...; return this;}
D doSomething() {int dummy; return doSomething(dummy);} // need this reimplementation to achieve covariance
}

That second function of D is a complete duplicate of code, and poses a maintenance nightmare. If this is a large hierarchy, any changes I make to the base wrapper must be duplicated in each derived class.

Not only that, but all versions of the function are exact copies! There is no difference in the generated code whatsoever.

With the advent of final functions in interfaces, I can achieve wrapper functions in an interface (I'm using that now for some things in dcollections). However, I can't make them covariant. So I'm forced to write the wrapper functions in the implementation instead of the interface, where it belongs.

It would be nice in both the interface and the class hierarchy cases to simply identify that a function is the same, but permanently covariant because it returns a pointer to this. Covariance always is possible if you return this.

That's all I'm saying.

-Steve

Reply via email to