Last update January 17, 2007

DWiki /
Covariant Return Types



Definition

The term Covariant return types means that an overriding function in a derived class can return a type that is directly derived from the type actually returned by that function. Functions which return types that are not derived from the overridden type cannot be used.

Classes

From the language spec:

Covariant return types for classes are supported directly in the language.

 class Widget { }
 class Frame : Widget { }

class Foo { Widget test() { return null; } }

class Bar : Foo { // This successfully overrides Foo's test() because it returns // a Frame which can be derived from a Widget. Thus it is said // to be covariant with Foo.test() Frame test() { return null; } }

Interfaces

Covariant return types for interfaces are supported via the template mechanism.

 interface iMutableList(T)
 {
   T opSlice(int a, int b);
 }
 interface iReproduce(T)
 {
   T dup();
 }

class myList : iMutableList!(myList), iReproduce!(myList) { myList opSlice(int a, int b) { . . . } myList dup() { . . . } . . . }

Without using templates here, the myList class would be forced to implement the interface functions so that they returned the precise (hard coded) types specified in the interface declarations.

For example this interface

 interface iReproduce
 {
   iReproduce dup();
 }
would mean that classes that implemented it would have to return an iReproduce rather than the class type.

Safe Casts

I'm not sure if this is the right place, but here is a workaround for the bug, NG:digitalmars.D.bugs/3287.

Some casts between interfaces and classes may crash because of the unsupported covariance.

The above bug can be avoided by a safe cast:

void* CIVTBL;  // Becomes pointer to vtable of the ClassInfo class

static this()
{
  CIVTBL = ClassInfo.classinfo.vtbl;
}


extern(C) Object _d_dynamic_cast(Object o, ClassInfo c);

/* Derived from _d_interface_cast in cast.d in phobos library.
*/
void* safecast_internal(void* p, ClassInfo c)
{
    Object o;

    if (p)
    {
        Interface *pi = **cast(Interface ***)p;

        // Additional check
        void* testvtbl = (*pi).classinfo;

        if (testvtbl == CIVTBL)
          o = cast(Object) p;    // Pointer to object, not interface
        else
          o = cast(Object) (p - pi.offset);

        return _d_dynamic_cast(o, c);
    }
    return null;
}


/*
  Dest: Type of class or interface to cast to

  s: must be of class or interface type. opCast() is never called
*/

template safecast(Dest)
{
  Dest safecast(void* s)
  {
    return cast(Dest) safecast_internal(s, Dest.classinfo);
  }
}

This cast first checks if the this reference (in fact a pointer) points to an interface (more precisely: points to the pointer to the vtable of an interface) or the object itself (points to the pointer to the vtable of the object).

Usage (refers to the example in the bug report):

Child mumsChild = safecast!(Child)(childsMum.test());  // Works
Child mumsChild2 = cast(Child) childsMum.test();  // Crashes

This is a workaround and does not solve the real underlying problem -- MichaelButscher?

Discussion

 StewartGordon June 1, 2005 17:20 CET
It would seem a bug, if not an arbitrary restriction, that an interface method can't be implemented with a covariant return type. Moreover, the template workaround isn't a general solution. For example, when using this approach one can't define a non-static class method to take an arbitrary iMutableList as parameter.
See: NG:digitalmars.D.bugs/1726

 DerekParnell 2nd June 2005 11:07 UTC+10
I can see your point and agree that the template approach is, at best, a workaround. I can't see why the compiler could not be a bit smarter to allow convariant return types. It might need a syntax change to tell the compiler your intentions, but that shouldn't be too difficult.


FrontPage | News | TestPage | MessageBoard | Search | Contributors | Folders | Index | Help | Preferences | Edit

Edit text of this page (date of last change: January 17, 2007 17:37 (diff))