Skip to main content
GameDev.net gamedev.net
🔒 Locked

[C++] Covariant return types + smart pointers

Started by Nitage Jun 26, 2006 at 9:35 AM 5 replies 6.3k views
Original Post
Nitage
Nitage
Is there any way to combine convariant return types with smart pointers? I currently have some code that is similar to this:

struct base
{
public:
    virtual base* clone()const = 0;
};

struct derived : base
{
    derived* clone()const
    {
        return new derived;
    }
};


I wanted to change the return type of the clone() function to use a smart pointer, but I can't get it to compile because the compiler/language doesn't recognise the covariance as it would for raw pointers...

struct base
{
public:
    virtual std::auto_ptr<base> clone()const = 0;
};

struct derived : base
{
    std::auto_ptr<derived> clone()const
    {
        return new derived;
    }
};


Does anyone know of a technique to bypass this limitation?
Fruny
Fruny
Even though Base and Derived are related, auto_ptr and auto_ptr aren't. You cannot use covariant returns.

I do not know of any workarounds. Use auto_ptr.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Nitage
Nitage
So it's choice between passing about raw pointers to dynamically allocated memory and having to dynamic_cast the result of clone() in some situations.

I'll probably choose to leave clone() as it is and just be careful to store the result in an auto_ptr of an appropriate type.
ToohrVyk
ToohrVyk
Covariant return types in C++ are quite limited in that you can only replace the base class type by a derived class type. So they are not a solution here.

However, you may use the following contraption with only a little higher cost:

class Base {protected:  virtual Base * cloneImpl() const = 0;public:  std::auto_ptr<Base> clone() const { return std::auto_ptr<Base>( this->cloneImpl() ); }};class Derived : public Base {protected:  virtual Derived * cloneImpl() const { return new Derived; }public:  std::auto_ptr<Derived> clone() const { return std::auto_ptr<Derived>( this->cloneImpl() ); }};


This way, covariant types are used through pointers in a protected area while fake covariant types are simulated by overriding.

EDIT: snk_kid, you may be faster but I put those two minutes to good use in formatting my answer! [grin] and you forgot the virtual and protected: details [rolleyes]
Nitage
Nitage
Thanks for the help.
Nitage
Nitage
This is what I have so far. Can anyone see any problem / potential improvements?

class cloneable{public:    virtual ~cloneable(){}    std::auto_ptr<cloneable> clone()const    {           return auto_clone<cloneable>();    }protected:    template <class T>    std::auto_ptr<T> auto_clone()const    {        // checks that we aren't object slicing        T* ptr = cloneImpl();        assert( typeid(*ptr) == typeid(*this) );        return std::auto_ptr<T>(ptr);    }    private:    //virtual clone operation    virtual cloneable* cloneImpl()const=0;    };


I've added in a check to prevent object slicing in the case where a child class forgets to implement a cloneImpl function when one of it's ancestor classes has implemented cloneImpl. Unfortunatley it's only a run-time check, I don't think it's possible to do the check at compile time.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.