Б. Страуструп - Язык программирования С++. Специальное издание, 3-изд. Бином. 2004 (1160791), страница 236
Текст из файла (страница 236)
— егазе() не генерирует исключений. Обратите внимание, что если для операции на контейнере обеспечивается сильная гарантия, все итераторы, указатели и ссылки на элементы при генерации исключения остаются действительными. Эти правила можно обьсдинить в таблицу: Гарантии операций с контейнерами Недие И51 тор оесгог с1еаг() не генерирует не генерирует не генерирует (копирование) не генерирует (коппрование) не генерирует (копирование) егазе() не генерирует ве генерирует не генерирует (копирование) 1-элементная йиегг() сильная сильная сильная сильная (копирование) (копирование) )ч"-элементная тзегг() сильная основная сильная сильная (копнрование) (копирование) глегяе() не генерирует (сравнение) сильная сильная сильная сильная сильная не генерирует не генерирует не генерирует (сравнение) гелосе (1() не генерирует (предикат) геоегхе() зр11се(! з шар() не генерирует не генерирует илн1ие() не генерирует (сравнение) В этой таблице: основная сильная не генерирует Там где гарантия требует, чтобы некоторые предоставленныс пользователем операции не генерировали исключений, зти операции указаны в круглых скобках под гарантией.
Эти требования точно сформулированы в тексте, предшествующем таблице. ризЬ Ьасй() рпзб ~голй) рор Ьасд() рор /гол1() гетоое() Приложение Д. Безопасность исключений и стандартная библиотека не генерирует не генерирует не генерирует не генерирует не генерирует не генерирует не генерирует (копирование сравнения) означает, гго операция обеспечивает только основную гарантию (б Д.2) означает, что операция обеспечивает сильнчю гарантию Я Д,2) означает, что операция не генерирует исключений (э Д.2) означает, что операция не обеспечивается как член данного контейнера 1041 Д.4.
Гарантии стандартных контейнеров Функция взвар() отличается от других упомянутых функций тем что не является членом. Гарантия для с1еаК) получена из предлагаемой для егазе() Ц 16.3.6). Таблица перечисляет гарантии, предоставляемые в дополнение к основной гарантии. Соответственно в таблицу не вошли операции, подобные геэегве[) и ип(дие(), для вес(ог, которые обеспечиваются только как алгоритмы для всех последовательностей без дополнительных гарантий.
<Почти контейнер» Ьав(с в(г(пу (Г1 17.5, ч 20.3) предлагает основную гарантию для всех операций (й Д 51). Стандарт также гарантирует, что для бав(с з/г(пдфункции егазе() и згоар() не генерируют исключений, а на функции )лзег1() и риз/з басй() распространяется сильная гарантия. Помимо неизменности контейнера, операция, обеспечивающая сильную гарантию, также гарантирует действительность всех оставляемых ею итераторов, указателей, и ссылок. Например: ооЫ ирбите(тар<зтг(лу, Х>й т, тир<все(пу, Х>зуегигог сиггепг) ( Хх; зтг1лу з; тЬ11е (сш»з»х) 1гу ( сштепг = тлпзег/(сиггеп1, тайе рпи(з, х)); сагсЛ (...) ( // здесь сипеп1 все еще обозниниет текущий зл ел~сит Д.4.2.
Гарантии и компромиссы Причудливая смесь дополнительных гарантий отражает подлинную сущность реализации. Программисты предпочитают сильную гарантию с как можно меньшим числом оговорок, но они также настаивают, чтобы каждая операция стандартной библиотеки была наиболее эффективна. Оба желания разумны, но для многих операций невозможно удовлетворить их одновременно. Чтобы дать лучшее представление о затронутых компромиссах, я исследую способы добавления одного или нескольких элементов к Йз1, иес1ог и тар.
Рассмотрим добавление единственного элемента к Йз/ или вес1ог. Как всегда, риз/с бас/е() обеспечивает самый простой способ: иоЫЯ11зс<Х>й 1з1, иес1ог<Х>й оес, солзгХ&х) ( ггу ( 1збризЬ бас/с(х), //добивленивк списку ) сагсЬ ( ..) ( // И не изяенен гегигп; ) 1гу ( иес.ризу Ьасй(х), // добавление к вектору Приложение Д. Безопасность исключений и стандартная библиотека 1042 сп!сЬ(,.) ( // иес не иэл!енен ге!игп ) // Ьд и иес. содерисагп по новому элелленту со эначен!лел~ х Обеспечение сильной гарантии в этих случаях просто и дешево.
Она очень полезна, потому что предоставляет полностью безопасный при исключениях способ добавления элементов. Однако ривй Ьас/с(~ не определена для ассоциативных контейнеров — у тар нет Ьас/с(), В конце концов, последний элемент ассоциативного контейнера определяется отношением порядка, а не позицией. Гарантии для !пзег!() немного запутаннее. Причина в том, что иногда лпзег!() приходится помешать элемент в «середину» контейнера. Это несложно для связной структуры данных, типа !!в! или тар. С другой стороны, если за оес!ог зарезервировано свободное пространство, очевидная реализация пес!ог<Х>"!пзег!() скопирует элементы после точки вставки, чтобы освободить место. Это наиболее аффективно, зато нет простого пути восстановления пес!ог, если присваивание копии Хили копирующий конструктор генерирует исключение (см. ЬД.8)10 — 11]).
Следовательно пес!ос обеспечивает гарантию, которая обусловлена тем, чтобы операция копирования элементов нс генерировала исключений. А !!з! и тар нс нуждаются в подобном условии; они запросто могут присоелиннть новые элементы уже после выполнения любого необходимого копирования. Как пример предположим, что присваивание копии и копирующий конструктор Х генерируют Хссаппо! сору, если они не могут успешно создать копию: иоЫ/(11з!<Х 8 !з1, сес!вг Х>й иес, тир<э!г!пу Х>с. и, сопзгХ8 х, сопз! з!г!пуй з) !гу ( !з!!л!зег!(!з!Ьеу!пй, х~; // добавление к сттку са!сд (...) ( ,1/ !з! не иэлсенен ге!игп; !гу ( пес !пзег!(пес Ьеу!и() х); // добавление к вектору ) са!сй (Х:саппо! сору) ( // Стоп: вес лижет содержать, и лижет и не содержании новый элемент са!сЬ (..) ( // иес не иэиенен ле!игп; !гу ( и !пзегг(таус Ра!г(з, х)); //добавление ктар 1О4З Д.4.Гарантии стандартных контейнеров са1сЬ (..
) ( // т не иэл~енен ге1игп; ) // Ьд и иве содержат по новом элементу со значением х // т содержит элемент со эничением Гэ, х) 1етрйэге<с/авэ С, с1авэ /гег> иоЫ эа~е /пэегг(СЙ с, 1урепате Сэсопв1 11ега1ог! ( С 1тр(с. Ьеу/п(), й), /гег Ьеугп, Нег епд) // копирование начал»них // элементов в 1тр // копирование нов»~х // элементов // копирование конечных // элел~ентов сору(Ьеугп, епд, 1пвегГег(!тр,1тр.епд()Ц сорЯ, с.епд(), ивеггег(1тр, 1тр.епд())) вэиар(с, 1тр); ) Как всегда, этот код может дурно себя вести, если деструктор элемента генерирует исключение.
Однако если исключение гснерируется операцией копирования элемен- та, контейнер-аргумент не изменяется. Если перехвачено Хссаппо1 сору, новый элемент либо вставнлся, либо не вставился в вес. Если новый элемент был вставлен, объект окажется в действителъном состоянии, но каково его значение — точно не определено, Возможно, послеХссаппо1 сору некоторый элемент окажется «мистически» продублирован !см. ЬД.8[11)).
Или 1пвег1() может быть реализована с удалением «хвостовых» элементов, чтобы в контейнере наверняка не осталось «посторонних». К сожалению, обеспечение сильной гарантии для )пвег1() в оес1ог без предостережения об исключениях, сгенерированных операциями копирования, не выполнимо. Стоимость полной защиты от исключения при перемещении элементов в оес1ог значительна сравнительно с обеспечением в этом случае лишь основной гарантии. Типы элементов с операциями копирования, которые могут генерировать искэпочения, не редки.
Примеры из самой стандартной библиотеки — оес1ог<в1г!пу>, эес1ог оес1ог<с/оиЫе» и тар<в1г1пу, гп1 . Контейнеры 1)в1 и оес1ог обеспечивают одинаковые гарантии при вставке как одного, так и многих элементов. Причина в том, что реализация спвег1() для оес1ог и 11в1 применяет одну и ту же стратегию вставки как одного, так и нескольких элементов. В то же время шар обеспечивает сильную гарантию для одноэлементной эпвег1(), но лишь основную гарантию для многоэлементной 1пвег1().
Легко реализовать вставку одного элемента в тар, которая обеспечивает сильную гарантию. Олнако очевидная стратегия для осуществления многоэлементной вставки втор состоит в том, чтобы вставлять новые элементы один за другим, а при этом не просто обеспечить сильную гарантию. Проблема в том, что нет простого способа отката предыдугцих успешных вставок, если вставка очередного элемента терпит неудачу. Функцию вставки, которая обеспечивает сильную гарантию !либо все элементы успешно добавлены, либо операция нн на что не повлияла), можно при желании построить, создав новый контейнер с последующим обменом вшар(): Приложение Д. Безопасность исключений и стандартная библиотека 1044 Д.4.3.
Перестановка з1зтар() Подобно копирующим конструкторам и присваиваниям, операции зшар() необходимы для многих стандартных алгоритмов и часто предоставляются пользователями. Например зог!() и з!аЫе зог!() обьгшо переупорядочивают элементы с помощью авар(). Таким образом, если функция яшар() генерирует исключение при перестановке значений из контейнера, этот контейнер может остаться с цепереставленными элементами или с дублированным элементом, а не с парой элементов, поменявшихся местами. Рассмотрим очевидное определение функции яшар() стандартной библиотеки Я 18.6.8): гетр!а!екс!азз Т> ооп7зшар(Ть а, Тй Ь) ( Тлпр = а, а =Ь, Ь =опр, Ясно что яшар() не генерирует исключений, если ни копирующий конструктор типа элемента, ни копиру|ощее присваиваиие не делают этого.