How to allow non-const copy constructor for temporaries

861 views Asked by At

How do I allow a class with a copy constructor that takes a non-const reference to be copy-constructed from temporaries?

The background is this:

I have a function that should return a list of pointers to objects that all inherit from Base, so I need something like vector<Base*>. Given that vector<auto_ptr> is not much of an option, I wanted to write a simple wrapper around vector<Base*> that deletes all elements in its destructor.

I'm facing the following problem:

My class has a copy constructor as follows:

auto_list(auto_list& rhs);

so that I can copy the pointer list to the new instance and clear it in the old one.

But obviously, that won't work with return values because temporaries don't bind to a non-const reference. Seeing that auto_ptr can be returned from functions, how did they implement it?

Note: I can't use C++11 or boost, so move semantics or unique_ptr are not an option.

If it helps, this is my code so far:

template <typename T> class auto_list
{
private:

    vector<T*> pointers;

public:

    auto_list(vector<T*>& pointers)
    {
        this->pointers = pointers;
    }

    auto_list(auto_list& rhs)
    {
        this->pointers = rhs.pointers;
        rhs.pointers.clear();
    }

    ~auto_list()
    {
        for(typename vector<T*>::const_iterator it = this->pointers.begin(); it != this->pointers.end(); it++)
        {
            delete (*it);
        }
    }

    auto_list& operator=(auto_list& rhs)
    {
        this->pointers = rhs.pointers;
        rhs.pointers.clear();
    }

    vector<T*> get_pointers() const
    {
        return this->pointers;
    }
};
4

There are 4 answers

0
Mike Seymour On BEST ANSWER

This class will be rather confusing to use, just as auto_ptr itself, and I urge you to use more sensible smart pointers. Even if you do have a good reason for not using Boost (and for the life of me I can't think why not), how about std::tr1::shared_ptr?

If you're determined to pursue this course, then auto_ptr solves the problem of initialising from a temporary by wrapping a reference in a class (auto_ptr_ref). The wrapper is created by a conversion function from *this (which is an lvalue, and therefore can be bound to a non-const reference), and can then be passed by value to a constructor.

You can do something similar:

template <typename T> class auto_list_ref
{
    friend class auto_list<T>;
    auto_list_ref(auto_list<T> & ref) : ref(ref) {}
    auto_list<T> & ref;
};

template <typename T> class auto_list
{
public:
    // Add a constructor and conversion operator as follows:

    auto_list(auto_list_ref<T> rhs)
    {
        this->pointers = rhs.ref.pointers;
        rhs.ref.pointers.clear();
    }

    operator auto_list_ref<T>() {return auto_list_ref<T>(*this);}
};

Here is a demonstration.

0
Puppy On

The whole reason that rvalue references were invented was because this cannot work in C++03. I mean, fundamentally, utterly, broken. You are trying to fight something that cannot be done. Stick to passing in return values or heap-allocated pointers.

2
Chris Dodd On

You could declare pointers as mutable, thus allowing you to declare your copy ctor and assignment ops as taking const auto_list & and still call clear. You need to use the resulting class carefully, however, as any copy will clear the object copied from.

0
LiKao On

If you are just working around the lack of std::vector<auto_ptr<Base>> with your auto_list, I suggest, you drop the class altogether and write your own counted pointers, which work with std::vector. If all you need is to store common objects of Base, you can even make it reference counted, so the code will be less than the custom list you are currently writing.

If this does not work, your second best choice probably is to adopt the broken way the standard handled unique_ptr<> before C++11. I.e. pass in a const reference and do a const_cast on it (yuck!!!). If you decide this, be very very very carefull to get the semantics right at all times (they will be broken enough by const things not being const).