Edit: Thanks to comments of Mateusz and A I have corrected code in this post (copy & paste error)

Covariant Return Types is language feature which allows you to change return type of your virtual function to covariant type ie. pointer to derived class instead of pointer to base class – see example below:

It compiles and works. Return type depends on what type method clone is called – if it is called via pointer or reference to Figure  it will return pointer to Figure  and if it is called via pointer or reference to Square  it will return pointer to Square . Simple and obvious, right?

What about Smart Pointers?

Since c++11 we want to avoid owning raw pointers so we would like to introduce interface which use ie. std::unique_ptr<Figure>  instead of Figure*.

Unfortunately smart pointers are not treated as covariant types so virtual std::unique_ptr<Square> clone() override; will not override virtual std::unique_ptr<Figure> clone() = 0;. My compiler (mingw with gcc-4.9) display errors:

Can we somehow introduce smart pointers and make use of covariant return types?

Clone function

To be able to use polymorphic magic we need to have clone()  method inside base class interface but we can also use some helper function which will wrap raw pointers into smart pointers ie.

Please notice that this function will work for every class which inherits Figure  interface – it takes argument by reference. We use such method like this:

It almost worked like previous solutions but it returns smart pointer to Figure  as f2  instead of smart pointer to Square . The problem is that clone function takes argument as reference to Figure which imply that it will call clone method via Figure reference so it will always return pointer to Figure from object.clone() call. We need to distinguish type on which clone function is called – let’s try templates.

and now…

It works just like previous example except it returns smart pointers instead of owning raw pointers. The only thing left is how to prevent user of caling Figure::clone function which still returns raw pointer – and in worse scenario make memory leak.

Preventing use of Figure::clone

Method Figure::clone  is public so user is allowed to call it. We can make it protected and then to make function clone able to call Figure::clone we can make it friend.

In such case we get such results:

When we call clone  function on Figure reference it works as expected but when called on Square it fails to work. We can put friend declaration to every class which inherits from Figure but this is not the best what we can do. Lets try to use fact that clone function works on Figure reference/pointer. We can do something like that:

And now everything works just like before. Unfortunately this code is not so generic – it can only work on classes which inherit Figure class. Lets make it more generic.

and change definition of Figure class to:

Now clone function works on every polymorphic type which has defined base_type attribute and virtual clone() method. Type T has to inherits T::base_type – we can make this restriction explicit by putting static_assert into the code:

Can we do better? Yes, we can introduce class which can be inherited to make your type cloneable.

Generic solution – cloneable type

Lets pack all requirements for Figure  interface to separate class – lets call it cloneable – and lets put there everything which need to be defined to make class cloneable. Then Figure class can inherit it and rest should work the same.

Everything works! Solution is not generic – we can eliminate dependency to Figure class by using template and Curiously recurring template pattern:

and that’s it! We can add helper method to be able to clone object via pointer instead of reference:

and we can put everything in one namespace ie. object then entire solution will look like this:

Expert question

Can we adjust this solution to take advantage of move semantics when clone function is called on rvalue objects?

Code

Code from this blog post (and some more examples and tests) can be found on here: https://github.com/CppCodeReviewers/Covariant-Return-Types-and-Smart-Pointers

About 

Service Delivery Manager and Scrum Master at Sii Poland company.

Since 2007 C++ enthusiast. C++11/14 evangelist - learn modern ways of developing in C++. Teaching TDD and agile way of coding.

  • googleplus
  • linkedin
  • twitter

15 comments on “Covariant Return Types and Smart Pointers

  • Starting from “PREVENTING USE OF FIGURE::CLONE” your code samples override with the return type of unique_ptr (instead of T*) which is probably the opposite of what you mean 😉

    • I do not understand your point of view. The whole idea is to be able to return smart pointer instead of raw pointer and to (somehow) prevent preserve covariant return type behaviour.

      In the end you get classes which are cloneable, return smart pointer instead of raw pointer and clonning behave similar to clonning with raw pointers (usage covariant return type).

      • Look at the return types of Figure::clone() and Square::clone() in the first code snippet under section “Preventing use of Figure::clone”. The return types should be Figure* and Square*, respectively. This is what Szymon is pointing out. The same mistake is happens in many code snippets after that as well.

  • 1. you said you still want covariant types, which is not possible, then in the comments you say you want to prevent covariant type behavior?

    2. not sure what you tried here, why not simply do something like this

    Chris

    • Ad 1. my mistake (it was late at night) prevent -> preserve :/ (corrected)
      Ad 2. this is almost what I am doing here but I would like to preserve ( 🙂 ) behaviour that when clone is called on Square object directly it should return

      instead of

      (like in your solution).

  • ok, I understand now

    but to just get unique_ptr can’t you get the same effect by simply doing this

    with no need for cloneable, crtp and helpers?

    • This will work. Only problem I want to solve is to disallow usage of clone() method which returns “not owning raw pointer” – it is unsafe. That’s why I make Figure* clone() method protected.

  • sure, you can make clone private in base class and friend with template clone to hide it from client code

    • This is exactly what I did. Unfortunately if you make your base class friend to some function it does not mean that child classes are friends too. Thats why in my implementation there is

      Only clone() function is friend only for base_type – so I did casting to base_type reference (then I can access clone method).

      Other option is to put friend declaration to each child class to allow access to clone() method.

      • Nope! clone(..) template function is friend for cloneable now. But if Figure class have clone() method there are compilation error !

  • Proposal for move semantic :


    using to_cast =std::conditional_t<
    std::is_lvalue_reference::value,
    base_type&,base_type&&
    >;

    return std::unique_ptr(static_cast(
    static_cast(object).clone()
    ));

    And :

    virtual T* clone() const & = 0;
    virtual T* clone() && = 0;

    Obviously drawback is the need to write :

    Square* clone() const Square* clone() &

    For each class. Simplest solution is a macro, but introduces two class (says SquareImpl and clone_impl) one could implement generic clone implementation. Nevertheless it seems overcomplicated.

Leave a Reply