Почему vector.push_back (auto_ptr) не компилируется?

Я узнал, что STL может запретить программисту помещать auto_ptr в контейнер. Например, следующий код не компилируется:

    auto_ptr<int> a(new int(10));
    vector<auto_ptr<int> > v;
    v.push_back(a);

auto_ptr имеет конструктор копирования, почему этот код вообще может компилироваться?


person frinker    schedule 09.09.2011    source источник
comment
Я знаю, что мне не следует использовать auto_ptr в stl из-за семантики копирования. Но у меня вопрос: как реализован stl, чтобы он мог вам это запретить? В моем примере кода он даже не компилируется.   -  person frinker    schedule 09.09.2011
comment
Вы можете опубликовать ошибку компиляции?   -  person R. Martinho Fernandes    schedule 09.09.2011
comment
@xanatos: нет конструкторов копирования const!   -  person Lightness Races in Orbit    schedule 09.09.2011
comment
@xanatos: Нет ссылок const! Они уже неизменны по своей сути!   -  person Lightness Races in Orbit    schedule 09.09.2011


Ответы (4)


Глядя на определение std::auto_ptr:

namespace std {

    template <class Y> struct auto_ptr_ref {};


    template <class X>
    class auto_ptr {
    public:
        typedef X element_type;

        // 20.4.5.1 construct/copy/destroy:
        explicit           auto_ptr(X* p =0) throw();
                           auto_ptr(auto_ptr&) throw();
        template <class Y> auto_ptr(auto_ptr<Y>&) throw();

        auto_ptr&                      operator=(auto_ptr&) throw();
        template <class Y> auto_ptr&   operator=(auto_ptr<Y>&) throw();
        auto_ptr&                      operator=(auto_ptr_ref<X>) throw();

        ~auto_ptr() throw();

        // 20.4.5.2 members:
        X&     operator*() const throw();
        X*     operator->() const throw();
        X*     get() const throw();
        X*     release() throw();
        void   reset(X* p =0) throw();

        // 20.4.5.3 conversions:
                                    auto_ptr(auto_ptr_ref<X>) throw();
        template <class Y> operator auto_ptr_ref<Y>() throw();
        template <class Y> operator auto_ptr<Y>() throw();
    };

}

Хотя существует конструктор копирования, он принимает ссылку на не const. Временные библиотеки могут не связываться с этим, поэтому типу эффективно запрещено работать внутри контейнеров в любом месте, где используются временные библиотеки; кроме того, push_back принимает ссылку на const, поэтому из-за const-корректности для нового внутреннего элемента невозможно скопировать его из аргумента push_back.

(На этой странице Википедии говорится, что «из-за своей семантики копирования auto_ptr не может использоваться в контейнерах STL, которые могут выполнять копии элементов в своих операциях»; это не означает, что контейнеры волшебным образом исследуют код внутри конструктора копирования, чтобы решить, действительно ли он хочет, чтобы тип работал как тип элемента. Вместо этого речь идет только о сигнатуре функции.)

В любом случае std::auto_ptr устарел, начиная с C ++ 11, потому что, по мнению некоторых, std::auto_ptr глупо. Извини, std::auto_ptr.

person Lightness Races in Orbit    schedule 09.09.2011
comment
Я категорически не согласен, std :: auto_ptr идеально подходит для решения ряда проблем. К сожалению, разработчики злоупотребляют этим. - person Alessandro Teruzzi; 09.09.2011
comment
@Alessandro: Если он настолько совершенен, почему он уже устарел? - person Lightness Races in Orbit; 09.09.2011
comment
@Alessandro: Я категорически не согласен, std :: unique_ptr идеально подходит для решения этого набора проблем. К счастью, злоупотребить им намного сложнее. :) - person R. Martinho Fernandes; 09.09.2011
comment
Я обычно использую std :: auto_ptr ‹›, чтобы явно указать на передачу прав собственности на объекты. Думаю, для этого это здорово. - person Jörgen Sigvardsson; 09.09.2011
comment
Я не думаю, что временные имеют какое-либо отношение к проблеме. Во-первых, в вопросе вызов push_back завершается неудачно, и нет временного участия: аргумент для push_back является константной ссылкой и используется в качестве аргумента для конструктора auto_ptr внутри, связывая const lvalue < / i> (не временно) на аргумент конструктора копирования завершается ошибкой, при этом в любой момент не задействованы временные объекты. - person David Rodríguez - dribeas; 09.09.2011
comment
@David: Время жизни контейнера не ограничивается push_back (хотя, по общему признанию, поскольку push_back принимает ref-to-const, это тоже проблема тоже). Будь то это или временные - а это, скорее всего, и то, и другое - не имеет значения: суть вопроса - ref-to-non-const. - person Lightness Races in Orbit; 09.09.2011
comment
@Р. Мартиньо Фернандес: std::unique_ptr - лучшее решение, но до C ++ 0x люди без особой причины злоупотребляли другими умными указателями. Подумайте, сколько shared_ptr вы видели, где владение является уникальным, а не общим ... Я сам довольно много видел, что или фабрики, которые возвращают shared_ptr, заставляя этот выбор умный указатель на пользователей ... Сказать, что std::auto_ptr глупо абсурдно, он служит / служит цели, а глупо пытается использовать его для вещей, для которых он никогда не проектировался. Черт, я хочу проголосовать против только за этот комментарий ... Но я не буду. - person David Rodríguez - dribeas; 09.09.2011
comment
@Tomalak Geret'kal: Да, время жизни объектов в контейнере больше, чем push_back в целом, но если вы не можете вставить объект в контейнер, время жизни очень мало в контейнер вообще. Время жизни могло бы стать проблемой, если бы вы, по крайней мере, смогли добавить значения в контейнер, а затем произошел сбой по любой другой причине. Опять же, я не могу думать о какой-либо операции std::vector, где будут созданы временные файлы типа T - я действительно не думал об этом, могут быть случаи. - person David Rodríguez - dribeas; 09.09.2011
comment
@ Дэвид: Не будь смешным! Это было не только явно в шутку, но и по какой-то причине auto_ptr устарел. Давайте перестанем сосредотачиваться на мелочах и рассмотрим более широкую точку зрения! - person Lightness Races in Orbit; 09.09.2011
comment
Потому что синтаксис необычный и вызывает путаницу (как и многие другие вещи в C ++). Это будет долгое обсуждение, по моему личному мнению, самая большая проблема с auto_ptr заключалась в том, что это был единственный smart_ptr в стандартной библиотеке, и разработчики пытаются использовать его ненадлежащим образом. - person Alessandro Teruzzi; 09.09.2011
comment
@Alessandro: Думаю, я согласен. В конечном счете, это очень субъективно, хорошо это auto_ptr или плохо, и любые дебаты по этому поводу будут неконструктивными. Я изложил свою точку зрения и высказал свое мнение в своем ответе; не стесняйтесь писать свои собственные противоположные ответы, но я не буду здесь обсуждать это! - person Lightness Races in Orbit; 09.09.2011
comment
@Tomalak: Я не голосовал против и не собирался делать это, даже если не могу понять шутку :) Я тоже не голосовал по причинам, которые я пытался объяснить: я не думаю, что есть какие-либо временные, участвующие в этом конкретном случае, и я не могу придумать причину, по которой std::vector мог создавать временные объекты внутри любой из операций. Обратите внимание, что resize(x) неявно создаст временный объект, если вы не предоставите второй аргумент, но он находится вне контейнера, и это просто ограничит операцию, которая ограничена для других типов, которые действительно работают в векторов - person David Rodríguez - dribeas; 09.09.2011
comment
@David: Я все еще не уверен, но поскольку push_back определенно вызывает проблему, я скорректирую свой пример. Спасибо за совет. - person Lightness Races in Orbit; 09.09.2011
comment
извините, я не понял, что это риторический вопрос ;-) - person Alessandro Teruzzi; 09.09.2011

Что касается конкретного вопроса о том, как компилятор обнаруживает эту ситуацию (или как STL вызывает там ошибку), вы должны прочитать точный вывод компилятора, он будет содержать кучу ошибок, которые приведут к сбою при выполнении преобразования. от const X до X, поскольку он отбрасывает квалификатор const, где X может быть либо std::auto_ptr<> напрямую, либо внутренним типом детали.

В частности, std::vector::push_back принимает аргумент const &, и внутренне он будет пытаться скопировать конструкцию элемента внутри динамического массива, используя доступный конструктор копирования, который в случае std::auto_ptr требует неконстантной ссылки. Что-то в строках:

void push_back( std::auto_ptr<int> const & x ) {
    // ensure enough capacity if needed...
    new (buffer + size()) std::auto_ptr<int>( x ); // !!! cannot bind x to non-const&
    // complete the operation (adjust end pointer, and such)
}
person David Rodríguez - dribeas    schedule 09.09.2011

Другие ответы касаются auto_ptr.

Чтобы сделать то, что вы пытаетесь сделать, используйте std :: unique_ptr, если он доступен вам (C ++ 11), если нет, вы можете использовать shared_ptr

person jk.    schedule 09.09.2011

Поскольку std :: auto_ptr несовместим с контейнером stl.

std :: auto_ptr использует семантику копии единого владения, контейнер stl должен скопировать конструкцию объекта (и некоторым алгоритмам необходимо назначить его)

Вы должны использовать умный указатель с подсчетом ссылок (boost :: shared_ptr)

ИЗМЕНИТЬ

Например, это подпись push_back

void push_back ( const T& x );

Проблема в том, что std :: auto_ptr особенный, а конструктор копирования и подпись оператора присваивания отличаются. Они НЕ являются константными. Вы изменяете auto_ptr, если копируете его.

auto_ptr& operator= (auto_ptr& a) throw();

auto_ptr (auto_ptr& a) throw();

Вы не можете предоставить auto_ptr, отвечающий требованиям push_back.

person Alessandro Teruzzi    schedule 09.09.2011
comment
Или приличный указатель единственного владения с семантикой перемещения, например std::unique_ptr. - person R. Martinho Fernandes; 09.09.2011
comment
взгляните на пункты 13-17 Эффективного C ++, они не связаны с auto_ptr, но очень полезны для понимания вашей проблемы. - person Alessandro Teruzzi; 09.09.2011
comment
Я знаю, что мне не следует использовать auto_ptr в stl из-за семантики копирования. Но у меня вопрос: как реализован stl, чтобы он мог вам это запретить? В моем примере кода он даже не компилируется. - person frinker; 09.09.2011
comment
Дело не только в push_back. Контейнер также должен копировать элементы внутри. - person Lightness Races in Orbit; 09.09.2011