Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use covariant return types with smart pointers?

I have code like this:

class RetInterface {...}  class Ret1: public RetInterface {...}  class AInterface {   public:      virtual boost::shared_ptr<RetInterface> get_r() const = 0;      ... };  class A1: public AInterface {   public:      boost::shared_ptr<Ret1> get_r() const {...}      ... }; 

This code does not compile.

In visual studio it raises

C2555: overriding virtual function return type differs and is not covariant

If I do not use boost::shared_ptr but return raw pointers, the code compiles (I understand this is due to covariant return types in C++). I can see the problem is because boost::shared_ptr of Ret1 is not derived from boost::shared_ptr of RetInterface. But I want to return boost::shared_ptr of Ret1 for use in other classes, else I must cast the returned value after the return.

  1. Am I doing something wrong?
  2. If not, why is the language like this - it should be extensible to handle conversion between smart pointers in this scenario? Is there a desirable workaround?
like image 256
amit kumar Avatar asked Oct 13 '08 04:10

amit kumar


People also ask

Which smart pointer type do you use when cycles need to be broken?

A std::weak_ptr helps to break the cycles of std::shared_ptr (R. 24). These cycles are the reason, a std::shared_ptr will not automatically release its resource.

How unique pointer is implemented?

unique_ptr<> is one of the Smart pointer implementation provided by c++11 to prevent memory leaks. A unique_ptr object wraps around a raw pointer and its responsible for its lifetime. When this object is destructed then in its destructor it deletes the associated raw pointer.


2 Answers

Firstly, this is indeed how it works in C++: the return type of a virtual function in a derived class must be the same as in the base class. There is the special exception that a function that returns a reference/pointer to some class X can be overridden by a function that returns a reference/pointer to a class that derives from X, but as you note this doesn't allow for smart pointers (such as shared_ptr), just for plain pointers.

If your interface RetInterface is sufficiently comprehensive, then you won't need to know the actual returned type in the calling code. In general it doesn't make sense anyway: the reason get_r is a virtual function in the first place is because you will be calling it through a pointer or reference to the base class AInterface, in which case you can't know what type the derived class would return. If you are calling this with an actual A1 reference, you can just create a separate get_r1 function in A1 that does what you need.

class A1: public AInterface {   public:      boost::shared_ptr<RetInterface> get_r() const      {          return get_r1();      }      boost::shared_ptr<Ret1> get_r1() const {...}      ... }; 

Alternatively, you can use the visitor pattern or something like my Dynamic Double Dispatch technique to pass a callback in to the returned object which can then invoke the callback with the correct type.

like image 165
Anthony Williams Avatar answered Oct 08 '22 00:10

Anthony Williams


There is a neat solution posted in this blog post (from Raoul Borges)

An excerpt of the bit prior to adding support for mulitple inheritance and abstract methods is:

template <typename Derived, typename Base> class clone_inherit<Derived, Base> : public Base { public:    std::unique_ptr<Derived> clone() const    {       return std::unique_ptr<Derived>(static_cast<Derived *>(this->clone_impl()));    }   private:    virtual clone_inherit * clone_impl() const override    {       return new Derived(*this);    } };  class concrete: public clone_inherit<concrete, cloneable> { };  int main() {    std::unique_ptr<concrete> c = std::make_unique<concrete>();    std::unique_ptr<concrete> cc = c->clone();      cloneable * p = c.get();    std::unique_ptr<clonable> pp = p->clone(); } 

I would encourage reading the full article. Its simply written and well explained.

like image 42
Bruce Adams Avatar answered Oct 08 '22 02:10

Bruce Adams