Может ли std :: vector emplace_back копировать конструкцию из элемента самого вектора?

При использовании push_back std::vector я могу нажать элемент самого вектора, не опасаясь аннулирования аргумента из-за перераспределения:

 std::vector v = { "a", "b" }; v.push_back(v[0]); // This is ok even if v.capacity() == 2 before this call. 

Однако, при использовании emplace_back , std::vector перенаправляет аргумент конструктору std::string чтобы конструкция копирования происходила на месте в векторе. Это заставляет меня подозревать, что перераспределение вектора происходит до того, как новая строка будет скопирована (иначе она не будет выделена на место), тем самым аннулируя аргумент перед использованием.

Означает ли это, что я не могу добавить элемент самого вектора с emplace_back или у нас есть какая-то гарантия в случае перераспределения, аналогичная push_back ?

В коде:

 std::vector v = { "a", "b" }; v.emplace_back(v[0]); // Is this valid, even if v.capacity() == 2 before this call? 

emplace_back должен быть безопасным по той же причине, чтобы push_back был безопасным ; недействительность указателей и ссылок действует только после того, как возвращается вызов метода модификации.

На практике это означает, что emplace_back выполняющий перераспределение, должен действовать в следующем порядке (игнорируя обработку ошибок):

  1. Выделить новую емкость
  2. Emplace – построить новый элемент в конце нового сегмента данных
  3. Переместить-построить существующие элементы в новый сегмент данных
  4. Уничтожить и освободить старый сегмент данных

В этом reddit-streamе STL подтверждает отказ VC11 поддерживать v.emplace_back(v[0]) как ошибку, поэтому вам обязательно нужно проверить, поддерживает ли ваша библиотека это использование и не воспринимает это как должное.

Обратите внимание, что некоторые формы самонастройки специально запрещены Стандартом; например, в пункте 4 [sequence.reqmts]. Таблица 100 a.insert(p,i,j) имеет необходимое условие: « i и j не являются iteratorами в ».

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

Вот пример кода, который может вызывать неопределенное поведение:

 std::vector v; v.push_back(0); // some more push backs into v followed but are not shown here... v.emplace_back(v.back()); // problem is here! 

Вышеприведенный код работает под Linux с g ++ STL без проблем.

При запуске одного и того же кода в Windows (скомпилированный с обновлением Visual Studio 2013 Update5) вектор иногда содержит некоторые искаженные элементы (по-видимому, случайные значения).

Причина в том, что ссылка, возвращаемая v.back() была признана недействительной из-за того, что контейнер достиг своего предела емкости внутри v.emplace_back() , прежде чем элемент был добавлен в конце.

Я просмотрел реализацию STL в VC ++ emplace_back() и, как представляется, выделил новое хранилище, скопировал поверх существующих векторных элементов в новое хранилище, освободил старое хранилище и затем создал элемент в конце нового хранилища. В этот момент базовая память ссылочного элемента может быть уже освобождена или иным образом недействительна. Это создавало неопределенное поведение, вызывая искажение векторных элементов, вставленных в пороги перераспределения.

Кажется, это ошибка (еще нефиксированная) в Visual Studio . С другими реализациями STL, которые я пытался, проблема не возникала.

В конце концов, вы должны избегать передачи ссылки на векторный элемент на emplace_back() этого же вектора на emplace_back() момент, по крайней мере, если ваш код будет скомпилирован с Visual Studio и должен работать.

Я проверил свою реализацию вектора, и он работает здесь следующим образом:

  1. Выделить новую память
  2. Вставить объект
  3. Старая память Dealloc

Так что все хорошо. Аналогичная реализация используется для push_back так что это один штраф два.

FYI, вот важная часть реализации. Я добавил комментарии:

 template template void vector<_Tp, _Alloc>:: _M_emplace_back_aux(_Args&&... __args) { const size_type __len = _M_check_len(size_type(1), "vector::_M_emplace_back_aux"); // HERE WE DO THE ALLOCATION pointer __new_start(this->_M_allocate(__len)); pointer __new_finish(__new_start); __try { // HERE WE EMPLACE THE ELEMENT _Alloc_traits::construct(this->_M_impl, __new_start + size(), std::forward<_args>(__args)...); __new_finish = 0; __new_finish = std::__uninitialized_move_if_noexcept_a (this->_M_impl._M_start, this->_M_impl._M_finish, __new_start, _M_get_Tp_allocator()); ++__new_finish; } __catch(...) { if (!__new_finish) _Alloc_traits::destroy(this->_M_impl, __new_start + size()); else std::_Destroy(__new_start, __new_finish, _M_get_Tp_allocator()); _M_deallocate(__new_start, __len); __throw_exception_again; } std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, _M_get_Tp_allocator()); // HERE WE DESTROY THE OLD MEMORY _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start); this->_M_impl._M_start = __new_start; this->_M_impl._M_finish = __new_finish; this->_M_impl._M_end_of_storage = __new_start + __len; } 
  • VectorDrawable - доступен ли он как-то для до-Lollipop версий Android?
  • Эффективный способ чтения файла в std :: vector ?
  • Заменить вектор, используя вектор индексов
  • Можно ли предположить, что хранилище векторов STL всегда смежное?
  • Как получить std :: векторный указатель на необработанные данные?
  • Каковы различия между ArrayList и Vector?
  • Сортировка вектора в порядке убывания
  • Как узнать, присутствует ли элемент в std :: vector?
  • C ++ Стереть векторный элемент по значению, а не по положению?
  • std :: vector, построение по умолчанию, C ++ 11 и нарушение изменений
  • Векторы в Arduino
  • Interesting Posts

    Каковы наилучшие имена файлов SQlite для расширений?

    Как я могу обойти «ошибку 0x80070522» при создании файлов в корневом каталоге диска C (C: \)?

    Конвертировать YouTube DASH audio (youtube-dl) в m4a (AAC DL)

    Использование ZXing для создания приложения сканирования штрих-кода для Android

    Исключение из памяти из-за большого размера растрового изображения

    Что такое кнопка безопасности Windows?

    Обнаружение события JTextField «отменить выбор»

    Что такое нерекурсивное решение для последовательности, подобной Фибоначчи, в Java?

    Выйти и перезапустить чистый сеанс R из R?

    Отсутствует ярлык программ из «Все программы»

    Как разблокировать установку программы в Windows 10

    Как создать стиль только с CSS?

    Какую кодировку я должен использовать для базовой проверки подлинности HTTP?

    Как заставить ConstraintLayout работать с процентными значениями?

    Изменение шрифта комментария в Microsoft Word 2007

    Давайте будем гением компьютера.