Return Type Covariance with Smart Pointers
In C++ we can do this:
struct Base
{
virtual Base* Clone() const { ... }
virtual ~Base(){}
};
struct Derived : Base
{
virtual Derived* Clone() const {...} //overrides Base::Clone
};
However, the following won't do the same trick:
struct Base
{
virtual shared_ptr<Base> Clone() const { ... }
virtual ~Base(){}
};
struct Derived : Base
{
virtual shared_ptr<Derived> Clone() const {...} //hides Base::Clone
};
In this example Derived::Clone
hides Base::Clone
rather than overrides it, because the standard says that the return type of an overriding member may change only from reference(or pointer) to base to reference (or pointer) to derived. Is there any clever workaround for this? Of course one could argue that the Clone
function should return a plain pointer anyway, but let's forget it for now - this is just an illustratory example. I am looking for a way to enable changing the return type of a virtual function from a smart pointer to Base
to a smart pointer to Derived
.
Thanks in advance!
Update: My second example indeed doesn't compile, thanks to Iammilind
You can't do it directly, but there are a couple of ways to simulate it, with the help of the Non-Virtual Interface idiom.
Use covariance on raw pointers, and then wrap them
struct Base
{
private:
virtual Base* doClone() const { ... }
public:
shared_ptr<Base> Clone() const { return shared_ptr<Base>(doClone()); }
virtual ~Base(){}
};
struct Derived : Base
{
private:
virtual Derived* doClone() const { ... }
public:
shared_ptr<Derived> Clone() const { return shared_ptr<Derived>(doClone()); }
};
This only works if you actually have a raw pointer to start off with.
Simulate covariance by casting
struct Base
{
private:
virtual shared_ptr<Base> doClone() const { ... }
public:
shared_ptr<Base> Clone() const { return doClone(); }
virtual ~Base(){}
};
struct Derived : Base
{
private:
virtual shared_ptr<Base> doClone() const { ... }
public:
shared_ptr<Derived> Clone() const
{ return static_pointer_cast<Derived>(doClone()); }
};
Here you must make sure that all overrides of Derived::doClone
do actually return pointers to Derived
or a class derived from it.
In this example
Derived::Clone
hidesBase::Clone
rather than overrides it
No, it doesn't hide it. Actually, it's a compilation error.
You cannot override nor hide a virtual function with another function that differs only on return type; so the return type should be the same or covariant, otherwise the program is illegal and hence the error.
So this implies that there is no other way by which we can convert shared_ptr<D>
into shared_ptr<B>
. The only way is to have B*
and D*
relationship (which you already ruled out in the question).
There is an improvement on a great answer by @ymett using CRTP technique. That way you needn't worry about forgetting to add a non-virtual function in the Derived.
struct Base
{
private:
virtual Base* doClone() const { ... }
public:
shared_ptr<Base> Clone() const { return shared_ptr<Base>(doClone()); }
virtual ~Base(){}
};
template<class T>
struct CRTP_Base : Base
{
public:
shared_ptr<T> Clone() const { return shared_ptr<T>(doClone()); }
};
struct Derived : public CRTP_Base<Derived>
{
private:
virtual Derived* doClone() const { ... }
};