С. Мейерс - Эффективный и современный C++ (1114942), страница 73
Текст из файла (страница 73)
push_bac k ( s td : : move ( spw) ) ;/ / Добавление1 1 как rvaluespwВерсия с использованием emplace _back аналогична:s t d : : sha red_ptr<Widget> spw ( new Widge t ,k i l lWidget ) ;ptrs . emplace back ( s td : : move ( spw) ) ;_В любом случае данный подход включает стоимость создания и удаления spw. С учетом того, что мотивацией для применения функций размещения вместо функций вставки является устранение стоимости создания и уничтожения временного объекта типа,хранящегося в контейнере (а spw концептуально и является таким объектом), функцииразмещения вряд ли превзойдут функции вставки при добавлении в контейнер объектов, управляющих ресурсами (если вы будете следовать хорошо проверенной практикеи гарантировать, что между захватом ресурса и превращением его в управляющий объект не будет выполняться никаких действий).8.2.
Рассмотрите применение размещения вместо вставки297Вторым важным аспектом функций размещения является их взаимодействие с конструкторами, объявленными как e xp l i c i t . Предположим, что вы создаете контейнеробъектов регулярных выражений С++ 1 1 :std : : vector<std: : regex> regexes ;Отвлекшись н а ссору ваших коллег о том, как часто следует проверять свой Facebook, выслучайно написали следующий, казалось бы, бессмысленный код:regexes . eшplace_Ьack (nullpt r ) ;Вы не заметили ошибку при вводе, компилятор скомпилировал его без замечаний, такчто вам пришлось потратить немало времени на отладку. В какой-то момент вы обнаружили, что вставляете в контейнер регулярных выражений нулевой указатель.
Но как этовозможно? Указатели не являются регулярными выражениями, и если вы попытаетесьнаписать что-то вродеstd : : regex r = nul lptr;/ / Ошибка ! Не компилируется !компилятор отвергнет такой код. Интересно, что будет отвергнут и вызов push_backвместо emplace back:_regexes . puвh_Ьack ( nullpt r ) ; // Ошибка ! Не компилируется !Такое любопытное поведение поясняется тем, что объекты std : : regex могут бытьпостроены из символьных строк.
Это делает корректным такой полезный код, какstd: : regex upperCaseWord ( " [A-Z] + " ) ;Создание s t d : : regex из символьной строки может иметь достаточно высокую стоимость, так что для минимизации вероятности непреднамеренных расходов конструкторs t d : : regex, принимающий указатель cons t cha r * , объявлен как expl i c i t . Именно поэтому не компилируются следующие строки:std: : regex rnul lptr;// Ошибка ! Не компилируется !regexes . push_back (nullptr ) ; // Ошибка ! Не компилируется !=В обоих случаях требуется неявное преобразование указателя в s t d : : regex, а объявление конструктора как exp l i c i t его предотвращает.Однако в вызове emplace back мы передаем не объект s t d : : regex, а аргументь1 конструктора объекта s t d : : regex. Это не рассматривается как запрос неявного преобразования. Компилятор трактует этот код так, как если бы вы написали_std : : regex r ( nul lptr ) ; / / КомпилируетсяЕсли лаконичный комментарий "Компилируется" кажется вам лишенным энтузиазма,то это хорошо, потому что, несмотря на компилируемость, данный код имеет неопределенное поведение.
Конструктор s t d : : regex, принимающий указатель const cha r * ,требует, чтобы этот указатель был ненулевым, а nu l lp t r подчеркнуто нарушает данноетребование. Если вы напишете и скомпилируете такой код, лучшее, на что вы можете298Глава 8. Тонкостинадеяться, - аварийное завершение программы во время выполнения. Если вы не такойсчастливчик, то вам предстоит получить немалый опыт работы с отладчиком.На минутку оставляя без внимания push _back и emp lace_back, обратим вниманиена то, что очень похожие синтаксисы инициализации дают совершенно разные результаты:std : : regex rlnullpt r ; // Ошибка ! Не компилируетсяstd : : regex r2 (nullpt r) ; // Компилируется=В официальной терминологии стандарта синтаксис, использованный для инициализации r l (со знаком равенства), соответствует инициализации копированием (соруiпitialization).
Синтаксис же, использованный для инициализации r2 (с круглыми скобками, хотя могут использоваться и фигурные), дает то, что называется прямой инициализацией (direct initialization). Инициализация копированием не может использовать конструкторы, объявленные как expl i ci t , в то время как прямая инициализация - может.Вот почему строка с инициализацией rl не компилируется, в отличие от строки с инициализацией r2.Но вернемся к push_ba c k и emplace_bac k и в более общем случае - к функциямвставки и размещения. Функции размещения используют прямую инициализацию, т.е.могут пользоваться конструкторами, объявленными как e xp l i c it . Функции вставкиприменяют инициализацию копированием, а потому использовать такие конструкторыне могут.regexes .
emplace_Ьack (nullpt r ) ; // Компилируется . Прямая// инициализация разрешает использовать конструктор// expl icit std : : regex, получающий указатель// Ошибка 1 Копируюшаяregexes . push_Ьack ( пullpt r ) ;// инициализация такие конструкторы не используетУрок, который следует извлечь из данного материала, состоит в том, что при использовании размещающих функций необходимо быть особенно осторожным и убедиться, чтофункциям передаются правильные аргументы, поскольку в ходе анализа кода будут рассмотрены даже конструкторы, объявленные как exp l i c i t .Сnедует запомнить•В принципе, функции размещения должны иногда быть более эффективными, чемсоответствующие функции вставки, и не должны быть менее эффективными.•На практике они чаще всего более быстрые, когда ( 1 ) добавляемое значение конструируется в контейнере, а не присваивается; (2) типы передаваемых аргументов отличаются от типа, хранящегося в контейнере; и (З) контейнер не отвергает дубликатыуже содержащихся в нем значений.•Функции размещения могут выполнять преобразования типов, отвергаемые функциями вставки.8.2.
Рассмотрите п рименение размещения вместо вставки299П р ед м етн ый указател ьNСимво лы=default, 1 20=delete, 85noexcept, 99условный, l 02Аnнllptr, 69оarray<>, 209async<>(), 245; 247override, 9 1стр атегии запуска, 249atomic<>, 1 1 3; 272auto, 23; 49рpackaged_task< > , 263Rauto_ptr<>, 1 26вrvalue, 1 6sЬind<>, 232; 237сshared_ptr<>, 1 3 3тconst, 1 06constexpr, 1 05thread<>(), 245const_iterator, 95thread_local, 2 5 1Ddecltype, 23; 36; 235true_type, 1 93typedef, 73typename, 75ЕenaЫe_if<>, 1 9 5uun ique_ptr<>, 1 26enum, 7 8using, 74explicit, 298Ffalse_type, 1 93vvector<>bool, 5 5final, 92forward<>(), 1 69; 204function<>, 5 1ошибка 11изайна,68volatile, 272; 277winitializer_ list<>, 34; 63weak_ptr<>, 1 42is_base_of<>, 1 98is_constructiЬle<>, 2 0 1Большая тройка,LВывод типа, 53lvalнe, 1 6мmake_shared(), 1 46make_uniqнe(), 1 4 6move(), 1 66mutaЬle, 1 1 2Б1 19вauto, 3 1decltype, 38шаблона, 23гГарантия безопасности исключениябазовая, 1 8строгая, 1 8дДиспетчеризация дескрипторов, 1 9 13Завершающий возвращаемый тип, 37Зависимый тип, 75Замыкание, 1 9; 22 1класс, 222иИдиомаPimpl, 1 32; 1 55RAII, 257захват ресурса есть иню1иализация, 257указателя на реализацию, 1 32; 1 55явной типизации инициализатора, 58Инициализациякопированием, 299прямая, 299унифицированная, 62фигурная, 62; 2 1 3и vector<>, 67Исключениеи деструктор, 1 03спецификация, 98Исключительное владение, 1 27Итератор, 95кКлассзамыкания, 222перечислений, 78шаблонный, 1 9Конструкторперемещающий, 1 1 7Контекстное ключевое слово, 92Контракт, 1 03лЛямбда-выражение, 22 1инициализирующий захват, 229и прямая передача, 235обобщенное, 234обобщенный захват, 231режим захвата, 222мМассив, 28Метапроrраммирование, 76; 1 94; 200302П редметный укаэательнНеопределенное поведение, 20; 4 1 ; 258; 274и предусловие, 1 04оОбъектвызываемый, 1 8со статическим временем хранения, 228функциональный, 1 8Объявление, 1 9псевдонима, 74Определение, 1 9Оптимизациявозвращаемого значения, 1 8 1избыточных чтений и записей, 276малых строк, 2 1 О; 289пПараллельные вычисления, 245ложное пробуждение, 266локальная память потока, 251общее состояние, 261переменная усло11ия, 265поток, 246Перекрытие, 88Перемещение, 1 1 7ограничения, 2 1 1почленное, 1 1 7; 2 1 0Перечисление, 78базовый тип, 79с об11астью видимости, 78Превышение подписки, 247Преобразование типанеявное сужающее, 63Прокси-класс, 56и auto, 57Прямая передача, 1 8; 1 65; 2 1 2; 235рРазмещение, 293сСвертывание ссылок.
1 75; 202; 203Совместное владение, 1 33Срезка, 290Ссылкапередаваемая, 1 72универса11ь11ая, 1 7 1 ; 190и перегрузка, 1 84ограничения, 1 94Ссылочн ы й квалификатор, 89; 92Счетчик ссылок, 1 33тТипбазовый перечисления, 79заоисимый, 75литера11ьный, 1 08llСПОЛНЫЙ, 1 56соойстоа, 76то11ько перемещаемый, 1 27; 165уУказательинтс1111ектуа11ьный, 20на функцию, 30обычный, 20Унифицированная и нициализация, 62фФункциональный объект. См.
ОбъектфункциональныйФункцияmake, 1 47аргумент, 1 8параметр, 1 8; 1 66lvalue, 1 69перекрытие, 88сигнатура, 1 9удаленная, 85ч11ен, специальная, 1 1 6шаблонная, 1 9Фьючерс, 245шШаблонвариативный, 2 1 3выражения, 57класса, 19отключенный, 1 95проектированиянаблюдатель, 145прокси, 57странно повторяющийся шаблон, 1 39функции, 1 9Предметны й указатель303ПРОГРАММИРОВАНИЕ.ПРИНЦИПЫ И ПРАКТИКА С ИСПОЛЬЗОВАНИЕМ С++ВТОРОЕ ИЗДАНИЕБьярне СтрауструпЭта к н и га - учебн и к попрогра м м и рован и ю. Несмотряна то что его автор - создател ьязы каС++,к н и га непосвящена этому язы ку; они грает в бол ьшей степен иилл юстрати в н у ю рол ь.
К н и газадумана как ввод н ы й курспо п рогра м м и рова н и ю сп р и мера м и програм м н ы хре ше н и й н а язы кеС+ +и оп исы вает ш и рок и йкруг пон яти й и п риемовп рогра м м и рова н и я ,необход и м ы х для того, чтобыстать п рофессионал ьн ы мп рогра м м истом.Впервую очередь к н и гаадресована нач и нающ и мwww.williamspuЫishing.comп рогра м м истам, н о о н а будетполезна и профессионалам,которые на йдут в ней м ногоновой и нформац и и , аглавное, смогут узнать точ кузрен и я создател я язы кана совре ме н н ые метод ып рогра м м и рова н и я .ISBN978-5-8459- 1 949-бвпродажеС++.