Почему неправильно использовать std :: auto_ptr <> со стандартными контейнерами?

217

Почему неправильно использовать std::auto_ptr<>стандартные контейнеры?

Uhall
источник
5
Определенно +1 на это, потому что я видел, как многие люди ошибаются. Это отличный вопрос.
Twokats
Пожалуйста, прочитайте также соответствующий пункт. Этот вопрос рассматривается здесь с другой стороны. Может быть полезно больше узнать о контейнерах auto_ptr и STL. stackoverflow.com/questions/8630552/…
Николай
1
moveсемантические и unique_ptrбыли разработаны, чтобы избежать проблем, связанных с auto_ptr. В C ++ 03 язык не был достаточно мощным, чтобы написать подобный класс, auto_ptrкоторый вел себя корректно и безопасно во всех сценариях, поскольку компилятор и язык не могли различать значения l и r, поэтому некоторые «хаки» использовались для получения желаемого поведения большую часть времени.
Phil1970
Хорошая статья: контейнеры STL и Auto_ptrs - почему они не смешиваются quantstart.com/articles/…
alfC

Ответы:

124

Стандарт C ++ говорит, что элемент STL должен быть «копируемым» и «присваиваемым». Другими словами, элемент должен быть в состоянии быть назначенным или скопированным, и эти два элемента логически независимы. std::auto_ptrне выполняет это требование.

Возьмите для примера этот код:

class X
{
};

std::vector<std::auto_ptr<X> > vecX;
vecX.push_back(new X);

std::auto_ptr<X> pX = vecX[0];  // vecX[0] is assigned NULL.

Чтобы преодолеть это ограничение, вы должны использовать std::unique_ptr, std::shared_ptrили std::weak_ptrумные указатели, или эквиваленты повышения, если у вас нет C ++ 11. Вот документация библиотеки поддержки для этих умных указателей.

Кевин
источник
7
Вы также должны рассмотреть контейнеры указателя наддува, если вам не нужно совместное владение.
me22
4
unique_ptrтакже запрещает копирование, поэтому некоторые операции STL не будут работать правильно, если они не могут использовать его семантику перемещения.
Майк Веллер
4
«Чтобы преодолеть это ограничение, вы должны использовать std::unique_ptr»: этот шаблон класса может существовать только из-за семантики перемещения (его спецификация требует ссылок на значения), поэтому он в основном требует C ++ 11. Однако (и связанный) стандарт C ++ 11 больше не говорит, что тип элемента STL должен быть «копируемым» и «присваиваемым»; быть способным к движению и назначаемым движением достаточно. Действительно, unique_ptrэкземпляры могут быть только конструируемыми и назначаемыми. Но так же, как и auto_ptrпримеры! Как следствие, в C ++ 11 вы можете делать auto_ptrто, что можете делать unique_ptr.
Марк ван Леувен
@MarcvanLeeuwen, если только вы resetи по releaseмере необходимости
чокнутый урод
2
@ratchetfreak: Хм, я не понимаю. Какой? «Если вы resetи release», я не вижу, как это относится к чему-либо в моем комментарии. Обратите внимание, что оба auto_ptrи unique_ptrимеют оба этих метода, и они делают одно и то же в обоих случаях.
Марк ван Леувен
66

В копировальной семантика из auto_ptrне совместимы с контейнерами.

В частности, копирование одного auto_ptrв другое не создает двух одинаковых объектов, поскольку один из них утратил право владения указателем.

Более конкретно, копирование auto_ptrприводит к тому, что одна из копий отпускает указатель. Что из этого остается в контейнере, не определено. Следовательно, вы можете случайно потерять доступ к указателям, если храните их auto_ptrsв контейнерах.

Фрэнк Крюгер
источник
39

Две супер отличные статьи на эту тему:

Lazer
источник
Потому что я думаю, что за прошедшие почти два года он, вероятно, занимался этой проблемой.
Щенок
27
@DeadMG: да, вы правы. Но это не было моей целью. Если кто-то когда-нибудь зайдет в эту ветку и захочет узнать auto_ptrи прочее, эти ссылки будут полезны, я уверен.
Лазер
Есть много дубликатов, которые более свежие.
Щенок
8
@DeadMG: Этот вопрос не был закрыт как дубликат и поэтому открыт для расширения. Лазер сказал то, что не было сказано раньше здесь. Я думаю, что он пришел случайно.
Себастьян Мах
Объяснения во второй ссылке, в которых анализируется проблема после звонка sort(), более понятны, чем все ответы здесь.
хаосинк
17

Контейнеры STL должны иметь возможность копировать элементы, которые вы храните в них, и должны рассчитывать на то, что оригинал и копия будут эквивалентны. Объекты с автоматическим указателем имеют совершенно другой договор, в результате чего копирование создает передачу права собственности. Это означает, что контейнеры auto_ptr будут демонстрировать странное поведение в зависимости от использования.

Подробное описание того, что может пойти не так, содержится в пункте 8 Effective STL (Скотт Мейерс), а также не очень подробное описание в пункте 13 Effective C ++ (Скотт Мейерс).

Гарт Гилмор
источник
12

Контейнеры STL хранят копии содержащихся предметов. Когда auto_ptr копируется, он устанавливает старый ptr в ноль. Многие методы контейнера нарушаются этим поведением.

Дастин Гетц
источник
Но при использовании unique_ptr вы получаете почти то же самое, поскольку только один unique_ptr может иметь право собственности на объект?
Tracer
2
@Tracer, unique_ptrкак и любой надлежащий объект C ++ 11, может передавать право собственности на свой ресурс только при создании или назначении перемещения, гарантируя, что программист должен преднамеренно передать значение std::move(sourceObject)или временное значение, а не передавать значение l и неинтуитивно / непредсказуемо его мутировать с помощью копирование-задание ... которое, как здесь тщательно подчеркивалось, было основной проблемой auto_ptr.
underscore_d
4

Стандарт C ++ 03 (ISO-IEC 14882-2003) гласит в пункте 3 пункта 20.4.5:

[...] [ Примечание: [...] auto_ptr не соответствует требованиям CopyConstructible и Assignable для элементов контейнера стандартной библиотеки и, следовательно, создание экземпляра контейнера стандартной библиотеки с auto_ptr приводит к неопределенному поведению. - конец примечания ]

Стандарт C ++ 11 (ISO-IEC 14882-2011) гласит в добавлении D.10.1 пункт 3:

[...] Примечание: [...] Экземпляры auto_ptr соответствуют требованиям MoveConstructible и MoveAssignable, но не соответствуют требованиям CopyConstructible и CopyAssignable. - конец примечания]

Стандарт C ++ 14 (ISO-IEC 14882-2014) гласит в приложении C.4.2 Приложение D: функции совместимости:

Изменение : шаблоны классов auto_ptr, unary_function и binary_function, шаблоны функций random_shuffle и шаблоны функций (и их возвращаемые типы) ptr_fun, mem_fun, mem_fun_ref, bind1st и bind2nd не определены.
Обоснование : заменено новыми функциями.
Эффект на исходную функцию : допустимый код C ++ 2014, который использует эти шаблоны классов и шаблоны функций, может не скомпилироваться в этом международном стандарте.

Битек
источник