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 элемент, который мы хотим вставить. То, что мы сразу видим в начале, - это проверка размера вектора, превышающего. Пока наша вставка заставляет вектор вырастить свой размер, мы получаем ссылку недействительной сразу после перераспределения. Это причина такого интересного, но ожидаемого (как только мы вошли в реализацию) поведения.