Как разрешить неконстантный конструктор копирования для временных файлов

Как разрешить создание класса с конструктором копирования, который принимает неконстантную ссылку, из временных объектов?

Фон такой:

У меня есть функция, которая должна возвращать список указателей на объекты, унаследованные от Base, поэтому мне нужно что-то вроде vector<Base*>. Учитывая, что vector<auto_ptr> не самый лучший вариант, я хотел написать простую обертку вокруг vector<Base*>, которая удаляет все элементы в его деструкторе.

Я столкнулся со следующей проблемой:

Мой класс имеет конструктор копирования следующим образом:

auto_list(auto_list& rhs);

чтобы я мог скопировать список указателей в новый экземпляр и очистить его в старом.

Но очевидно, что это не будет работать с возвращаемыми значениями, потому что временные объекты не привязываются к неконстантным ссылкам. Увидев, что auto_ptr можно вернуть из функций, как они это реализовали?

Примечание. Я не могу использовать C++11 или boost, поэтому семантика перемещения или unique_ptr не подходят.

Если это поможет, это мой код:

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;
    }
};

person pezcode    schedule 12.01.2012    source источник


Ответы (4)


Этот класс будет довольно запутанным в использовании, как и сам auto_ptr, и я призываю вас использовать более разумные интеллектуальные указатели. Даже если у вас есть веская причина не использовать Boost (и клянусь жизнью, почему бы и нет), как насчет std::tr1::shared_ptr?

Если вы полны решимости продолжить этот курс, то auto_ptr решает проблему инициализации из временного объекта, заключая ссылку в класс (auto_ptr_ref). Обертка создается функцией преобразования из *this (которая является lvalue и, следовательно, может быть привязана к неконстантной ссылке), а затем может быть передана по значению в конструктор.

Вы можете сделать что-то подобное:

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);}
};

Вот демонстрация.

person Mike Seymour    schedule 12.01.2012

Вся причина, по которой были изобретены ссылки rvalue, заключалась в том, что это не может работать в C++03. Я имею в виду, принципиально, совершенно, сломанный. Вы пытаетесь бороться с чем-то, что невозможно сделать. Придерживайтесь передачи возвращаемых значений или указателей, выделенных в куче.

person Puppy    schedule 12.01.2012

Вы можете объявить pointers как mutable, что позволит вам объявить ваш ctor копирования и операции назначения как принимающие const auto_list & и по-прежнему вызывать очистку. Однако вам нужно осторожно использовать полученный класс, так как любая копия удалит объект, из которого он был скопирован.

person Chris Dodd    schedule 12.01.2012
comment
На самом деле это кажется довольно простым решением. Есть ли что-то, что может взорваться? Увидев ответ Майка Сеймура, мне интересно, почему auto_ptr усложняет такие вещи вместо использования mutable. - person pezcode; 12.01.2012
comment
Тогда вы больше не сможете объявить его const, чтобы предотвратить аннулирование, что сделает его еще более запутанным и подверженным ошибкам, чем auto_ptr. Его можно сделать недействительным, передав его любой функции, даже если функция принимает ссылку const. - person Mike Seymour; 12.01.2012

Если вы просто работаете над отсутствием std::vector<auto_ptr<Base>> в своем auto_list, я предлагаю вам вообще отказаться от класса и написать свои собственные подсчитываемые указатели, которые работают с std::vector. Если все, что вам нужно, это хранить общие объекты Base, вы даже можете сделать его подсчетом ссылок, поэтому код будет меньше, чем пользовательский список, который вы сейчас пишете.

Если это не сработает, ваш второй лучший выбор, вероятно, состоит в том, чтобы принять неверный способ, которым стандарт обрабатывал unique_ptr<> до C++11. т.е. передать const reference и сделать const_cast на нем (фу!!!). Если вы решите это, будьте очень, очень, очень осторожны, чтобы всегда правильно понимать семантику (они будут достаточно нарушены из-за того, что константные вещи не являются константами).

person LiKao    schedule 12.01.2012