Почему добавление объекта std::deque к себе через std::copy успешно, если deque недостаточно велик?
Книга права, я просто неправильно прочитал строку.
Как ясно указывает ответ @uneven_mark, следующий вопрос зависит от моего неправильного прочтения.
Читая Стандартную библиотеку C++ (2-е издание) Джосуттиса, я как-то убедился в том, что coll
на странице 457 объявлен как std::deque
(наоборот, он объявлен как std::list
!), следовательно, я задал этот вопрос.
Я надеюсь, что это может послужить пищей для размышлений для читателей.
ОРИГИНАЛЬНЫЙ ВОПРОС:
На странице 456 "Стандартной библиотеки C++ (2-е издание)" Джозуттис отмечает, что перед тем, как позвонить вам
copy(coll.begin(), coll.end(), back_inserter(coll));
на coll
класса std::vector
, вы должны убедиться, что coll
имеет достаточно места (в этом случае, что он имеет capacity
по крайней мере, в два раза size
), в противном случае
алгоритм аннулирует переданные исходные итераторы во время работы.
Напротив, на странице 458 он не говорит ничего подобного для случая
copy(coll.begin(), coll.end(), front_inserter(coll));
применительно к coll
класса std::deque
хотя на странице 286 указано следующее std::deque
контейнер:
[...] когда элементы вставляются спереди или сзади. В этом случае ссылки и указатели на элементы остаются действительными, а итераторы - нет.
отсюда мое сомнение. (Да, я знаю std::deque
даже не предлагает reserve
-подобная функция-член.)
Пока я понял этот ответ, я понимаю, что front_inserter(coll)
итератор может вызвать перераспределение массива указателей (что является законным способом реализации std::deque
), и не может вызвать перераспределение массивов, в которых coll
сохраняются, таким образом, оставляя ссылки / указатели на элементы действительными, в то же время аннулируя iterator
ы, чье правильное поведение (я думаю о том, как operator++
может быть реализовано) опирается как на массив указателей, так и на указатели на массивы.
Если это правда, то я предполагаю, что параметр, соответствующий copy
аргумент coll.begin()
становится недействительным в тот момент, когда присвоение ему вызывает перераспределение массива указателей.
1 ответ
Страница 455/456 книги представляет std::back_inserter
в то время как страница 457/458 вводит std::front_insert
, В каждом случае есть краткое объяснение, включая список применимых контейнеров. Каждый раздел имеет фрагмент кода в качестве примера, и только один из применимых контейнеров выбран для иллюстрации использования.
За std::back_inserter
как контейнер std::vector
выбирается и комментарий в фрагменте кода упоминает, почему необходимо сначала зарезервировать достаточно места в векторе.
За std::front_inserter
автор выбрал std::list
не std::deque
, std::list
не делает недействительными ссылки или итераторы при вставке, поэтому
copy(coll.begin(), coll.end(), front_inserter(coll));
в порядке, см. [list.modifiers]:
Примечания: не влияет на достоверность итераторов и ссылок. Если выброшено исключение, никаких эффектов нет.
Поэтому в обоих случаях нет ошибки в авторском коде. Я полагаю, что он никогда не намеревался полностью объяснить опасность копирования в сам контейнер, а просто выбрал эти случаи, потому что это позволило ему написать более короткие полные примеры использования.
Я думаю для случая, когда coll
это std::deque
это явно неопределенное поведение. std::front_inserter
вставляет элементы по вызовам push_front
[front.insert.iter.ops]
Эффекты: Как будто by: container->push_front (value);
, который делает недействительными все итераторы [deque.modifiers]:
Эффекты: вставка в середине deque делает недействительными все итераторы и ссылки на элементы deque. Вставка в любом конце deque делает недействительными все итераторы в deque, но не влияет на достоверность ссылок на элементы deque.
В то же время std::copy
s поведение [alg.copy]:
Эффекты: копирует элементы из диапазона [first, last) в диапазон [result, result + N), начиная с первого и заканчивая последним. Для каждого неотрицательного целого числа n
После первой вставки first
является недействительным, и будет вызвано неопределенное поведение.