Emplace_back() в VS2013

Рассмотрим следующие коды

std::vector<int> nums{21, 22, 23, 24};
nums.emplace_back(nums[0]);
nums.emplace_back(nums[1]);

for (auto n : nums) {
    std::cout << n << std::endl;
}

Вывод VS2013

21
22
23
24
-17891602
22

Почему -17891602 здесь?

Вывод GCC 4.8.4 верен следующим образом

21
22
23
24
21
22

Затем я сравниваю реализацию emplace_back между VS2013 и GCC

VS2013

template<class... _Valty>
    void emplace_back(_Valty&&... _Val)
    {   // insert by moving into element at end
    if (this->_Mylast == this->_Myend)
        _Reserve(1);
    _Orphan_range(this->_Mylast, this->_Mylast);
    this->_Getal().construct(this->_Mylast,
        _STD forward<_Valty>(_Val)...);
    ++this->_Mylast;
    }

НКУ

template<typename _Tp, typename _Alloc>
template<typename... _Args>
  void
  vector<_Tp, _Alloc>::
  emplace_back(_Args&&... __args)
  {
    if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
      {
        _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
                                 std::forward<_Args>(__args)...);
        ++this->_M_impl._M_finish;
      }
    else
      _M_emplace_back_aux(std::forward<_Args>(__args)...);
  }

Кажется, странный _Reserve(1); используется в VS2013. Почему?

Edit:

Значение hex -17891602 равно 0xFEEEFEEE, что означает

Используется Microsoft debug HeapFree() для отметки освобожденной памяти кучи

обратитесь к магическому номеру

Затем я отлаживал приведенные выше строки по строкам и обнаружил вызванный 0xFEEEFEEE вызванный _Reserve(1);.

Ответы

Ответ 1

Это проблема в VS2013 и VS2015 при установке элемента в вектор, содержащий этот элемент. Если вектор изменяет размер, ссылка на вставленный элемент недействительна. Работа вокруг заключается в создании копии элемента во вставке, а затем вставьте его.

auto n = nums[0];
nums.emplace_back(n);

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

Ответ 2

Проблема emplace

Объекты, привязанные к набору параметров функции функции emplace, должны не быть элементами или под-объектами элементов контейнера.

emplace_back() вызывается в emplace() в функции VS2013.

  template<class... _Valty>
    iterator emplace(const_iterator _Where, _Valty&&... _Val)
    {   // insert by moving _Val at _Where
    size_type _Off = _VIPTR(_Where) - this->_Myfirst;

 #if _ITERATOR_DEBUG_LEVEL == 2
    if (size() < _Off)
        _DEBUG_ERROR("vector emplace iterator outside range");
 #endif /* _ITERATOR_DEBUG_LEVEL == 2 */

    emplace_back(_STD forward<_Valty>(_Val)...);
    _STD rotate(begin() + _Off, end() - 1, end());
    return (begin() + _Off);
    } 

Забастовкa >

Я нашел одно хорошее сообщение , в котором описываются некоторые детали реализации emplace_back() в VS2013.

std::vector класс имеет разные члены экземпляра (как регулярные, так и внутренние), и среди них следующие:

  • _Myfirst - указывает на начало массива данных
  • _Mylast - указывает на первый неинициализированный элемент в массиве данных. Если значение равно _Myend, следующая вставка вызовет перераспределение. Вы вызываете этого парня на вызов end()
  • _Myend - указывает на конец массива данных

Итак, с точки зрения адресов памяти имеет место следующее неравенство:

_Myfirst <=<= _Mylast <=<= _Myend

Посмотрите на эту строку с _Reserve(1) в ней? Этот вызов функции заставляет нашу ошибку проявлять себя.

Позвольте работать шаг за шагом (см. предыдущую функцию примера).

nums.emplace_back(nums[0]);

Сначала мы получаем ссылку , потому что operator[] возвращает reference

reference operator[](size_type _Pos) 
{ ... }

Затем переходим в метод emplace_back, передавая свежий и действительный reference элемент, который мы хотим вставить. То, что мы сразу видим в начале, - это проверка размера вектора, превышающего. Пока наша вставка заставляет вектор вырастить свой размер, мы получаем ссылку недействительной сразу после перераспределения. Это причина такого интересного, но ожидаемого (как только мы вошли в реализацию) поведения.