36

This question was asked here a few hours ago and made me realise that I have never actually used covariant return types in my own code. For those not sure what covariance is, it's allowing the return type of (typically) virtual functions to differ provided the types are part of the same inheritance hierarchy. For example:

struct A {
   virtual ~A();
   virtual A * f();
   ...
};

struct B : public A {
   virtual B * f();
   ...
};

The different return types of the two f() functions are said to be covariant. Older versions of C++ required the return types to be the same, so B would have to look like:

struct B : public A {
   virtual A * f();
   ...
};

So, my question: Does anyone have a real-world example where covariant return types of virtual functions are required, or produce a superior solution to simply returning a base pointer or reference?

Community
  • 1
  • 1

6 Answers6

37

The canonical example is a .clone()/.copy() method. So you can always do

 obj = obj->copy();

regardless what obj's type is.

Edit: This clone method would be defined in the Object base class (as it actually is in Java). So if clone wasn't covariant, you would either have to cast, or would be restricted to methods of the root base class (which would have only very few methods, compared the class of the source object of the copy).

Martin v. Löwis
  • 124,830
  • 17
  • 198
  • 235
  • 2
    But you shouldn't (IMHO) care what the type is A * a = some_a_or_b->clone(); works just as well, and you then use other virtual methods on the cloned pointer. –  Aug 11 '09 at 14:42
  • 2
    I see covariance more as syntactic sugar, then as a needed feature. You can always do A* a = b->clone(); dynamic_cast( a )->initB(); – Christopher Aug 11 '09 at 14:51
  • 11
    Excepted that with covariance the type is statically checked. – AProgrammer Aug 11 '09 at 15:05
  • 4
    IMO, casts of any sort are ugly and kludgy and really should be avoided as much as possible. With the dynamic_cast here's no compile-time check that B::clone really does what the method name implies and returns a B* (as opposed to a pointer to some other subclass of A). – Tyler McHenry Aug 11 '09 at 15:06
  • 3
    This example makes sense with Java's crippled type system, but I still don't see the relevance in C++. – jalf Aug 11 '09 at 15:49
  • @jalf: which part of Java do you consider crippled? That there is a common Object base class that has very few methods, or that one of the methods is .clone()? If the former: there are several C++ libraries that define an Object base class with a small interface, e.g. Qt (QObject) and MFC (CObject). – Martin v. Löwis Aug 11 '09 at 16:09
  • @Christopher, why would you want to upcast and then downcast the object with dynamic_cast? That would be not only ugly but also inefficient because of the dynamic_cast overhead. – Fernando N. Aug 11 '09 at 22:45
  • @Martinv.Löwis you don't need covariant clone when you have copy construction. – Caleth Aug 18 '20 at 17:51
  • @Caleth you can't have virtual constructor, actually `clone` method is a `virtual constructor idiom`. Meaning if have only point to a base class you're not able to copy your object wo using casts. – Soup Endless Mar 04 '21 at 11:32
17

In general, covariance allows you to express more information in the derived class interface than is true in the base class interface. The behaviour of a derived class is more specific than that of a base class, and covariance expresses (one aspect of) the difference.

It's useful when you have related hierarchies of gubbins, in situations where some clients will want to use a base class interface, but other clients will use the derived class interface. With const-correctness omitted:

class URI { /* stuff */ };

class HttpAddress : public URI {
    bool hasQueryParam(string);
    string &getQueryParam(string);
};

class Resource {
    virtual URI &getIdentifier();
};

class WebPage : public Resource {
    virtual HttpAddress &getIdentifier();
};

Clients which know they have a WebPage (browsers, perhaps) know that it's meaningful to look at query params. Clients which are using the Resource base class know no such thing. They will always bind the returned HttpAddress& to a URI& variable or temporary.

If they suspect, but don't know, that their Resource object has an HttpAddress, then they can dynamic_cast. But covariance is superior to "just knowing" and doing the cast for the same reason that static typing is useful at all.

There are alternatives - stick the getQueryParam function on URI but make hasQueryParam return false for everything (clutters the URI interface). Leave WebPage::getIdentifier defined to return URL&, actually returning an HttpIdentifier&, and have callers do a pointless dynamic_cast (clutters the calling code, and the documentation of WebPage where you say "the URL returned is guaranteed to be dynamically castable to HttpAddress"). Add a getHttpIdentifier function to WebPage (clutters the WebPage interface). Or just use covariance for what it's meant to do, which is express the fact that a WebPage doesn't have an FtpAddress or a MailtoAddress, it has an HttpAddress.

Finally there is of course a reasonable argument that you shouldn't have hierarchies of gubbins, let alone related hierarchies of gubbins. But those classes could just as easily be interfaces with pure virtual methods, so I don't think it affects the validity of using covariance.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • 2
    gubbins? That's not a word I'm familiar with. Googling it produces this from UrbanDictionary.com: A gubbin is someone who on occasion acts like a vagrant. "You wouldn't believe his parents own a mansion would you? The gubbin!" I presume there is a different meaning in a programming context? – Jeremy Friesner Aug 11 '09 at 22:17
  • 1
    'fraid not. It just means "stuff", "things". – Steve Jessop Aug 12 '09 at 10:33
6

I think covariance can be useful when declaring factory methods that return a specific class and not its base class. This article explains this scenario quite well, and includes the following code example:

class product
{
    ...
};

class factory
{
public:
    virtual product *create() const = 0;
    ...
};

class concrete_product : public product
{
    ...
};

class concrete_factory : public factory
{
public:
    virtual concrete_product *create() const
    {
        return new concrete_product;
    }
    ...
};
Alan
  • 13,510
  • 9
  • 44
  • 50
2

I often find myself using covariance when working with existing code to get rid of static_casts. Usually the situation is similar to this:

class IPart {};

class IThing {
public:
  virtual IPart* part() = 0;
};

class AFooPart : public IPart {
public:
  void doThis();
};

class AFooThing : public IThing {
  virtual AFooPart* part() {...}
};

class ABarPart : public IPart {
public:
  void doThat();
};

class ABarThing : public IThing {
  virtual ABarPart* part() {...}
};    

This allows me to

AFooThing* pFooThing = ...;
pFooThing->Part()->doThis();

and

ABarThing pBarThing = ...;
pBarThing->Part()->doThat();

instead of

static_cast< AFooPart >(pFooThing->Part())->doThis();

and

static_cast< ABarPart >(pBarThing->Part())->doThat();

Now when coming across such code one could argue about the original design and whether there is a better one – but in my experience there are often constraints such as priorities, cost/benefit etc. which interfere with extensive design beautification and permit only small steps such as this one.

Tobias
  • 6,388
  • 4
  • 39
  • 64
0

Another example is a concrete factory which would returns pointers to the concrete classes instead of the abstract one (I've used it for internal use in the factory when the factory had to construct compound objects).

AProgrammer
  • 51,233
  • 8
  • 91
  • 143
  • Presumably you dynamic_cast the pointers to work out what concrete instance you have actually got? In which case you don't need the covariance. Or am I missing something? –  Aug 11 '09 at 14:50
  • 'dynamic_cast' requires that the types are polymorphic and it is potentially expensive. However, the same result can be achieved using a function template in the factory class. This would perform two tasks: Check that the types are related (isbaseclass or whatever) and then cast the generic result down to the derived type. No dynamic cast necessary. – Richard Corden Aug 11 '09 at 15:02
  • By potentially expensive I mean more expensive than a static_cast and even then it's only where you're calling it millions of times that it makes a difference. ;) – Richard Corden Aug 11 '09 at 15:03
  • You can imagine also a visitor like pattern. But in the case I've used, it was within the factory itself that covariance was used. Obviously, the factory know its own dynamic type (I haven't yet used two levels of inheritance in a factory :-() – AProgrammer Aug 11 '09 at 15:03
  • 2
    @Richard: I think Scott Meyers once pointed out that using `dynamic_cast` in VS can bring you down to string comparison. (IIRC; That's when DLLs are involved.) This is surely orders of magnitudes slower that `static_cast`. – sbi Aug 11 '09 at 19:02
0

It becomes useful in the scenario where you want to use a concrete factory to generate concrete products. You always want the most specialized interface that is general enough...

The code using the concrete factory can safely suppose that the products are concrete products, so it can safely use the extensions provided by the concrete product class with regard to the abstract class. This could indeed be regarded as syntactic sugar, but it's sweet anyway.

xtofl
  • 40,723
  • 12
  • 105
  • 192