c++ - problema emplace_back() bajo VS2013
c++11 gcc (2)
Este es un problema en VS2013 y VS2015 cuando se emplaza un elemento en un vector que contiene el elemento. Si el vector cambia de tamaño, la referencia al elemento que se inserta no es válida. El trabajo consiste en crear una copia del elemento en el inserto, luego insértelo.
auto n = nums[0];
nums.emplace_back(n);
La llamada _Reservar está allí para garantizar que haya algo de memoria asignada para el vector (por lo que no es necesario verificarla en operaciones posteriores).
Considere los siguientes códigos
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;
}
Salida de VS2013
21
22
23
24
-17891602
22
¿Por qué el -17891602
está aquí?
La salida de GCC 4.8.4
es correcta como sigue
21
22
23
24
21
22
Luego comparo la implementación de emplace_back
entre VS2013
y 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;
}
GCC
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)...);
}
Parece la extraña _Reserve(1);
Se utiliza en VS2013
. ¿Por qué?
Editar:
El valor hex
de -17891602
es 0xFEEEFEEE
, que significa
Utilizado por la depuración HeapFree () de Microsoft para marcar la memoria del montón liberado
refiérase al número mágico
Luego depuré los códigos anteriores línea por línea y encontré el 0XFEEEFEEE
causado por _Reserve(1);
invocado
Los objetos vinculados al paquete de parámetros de función de la función miembro
emplace
no serán elementos o subobjetos de elementos del contenedor.
El emplace_back()
se llama en la función VS2013
emplace()
bajo 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);
}
Encontré una buena post , que describe algunos detalles de la implementación emplace_back()
bajo VS2013
.
std::vector
class tiene diferentes miembros de instancia (tanto regulares como internos) y entre ellos se encuentran los siguientes:
-
_Myfirst
- apunta al comienzo de la matriz de datos -
_Mylast
- apunta al primer elemento sin inicializar en la matriz de datos. Si es igual a _Miend, la siguiente inserción causará la reasignación. Tienes a este tipo alend()
llamada -
_Myend
- apunta al final de la matriz de datos
Entonces, en términos de direcciones de memoria, tiene lugar la siguiente desigualdad:
_Myfirst <=<= _Mylast <=<= _Myend
¿Ves esa línea con _Reserve(1)
en ella? Esta llamada de función hace que nuestro error se revele.
Trabajemos paso a paso (consulte la función de ejemplo anterior).
nums.emplace_back(nums[0]);
Primero obtenemos una referencia al artículo porque el operator[]
devuelve una reference
reference operator[](size_type _Pos)
{ ... }
Luego pasamos al método emplace_back
, pasando una referencia nueva y válida al elemento que queremos insertar. Lo que vemos inmediatamente al principio es una verificación del tamaño del vector que excede. Mientras nuestra inserción haga que un vector crezca su tamaño, obtendremos una referencia invalidada justo después de que ocurra la reasignación . Esa es la razón de un comportamiento tan interesante pero esperado (una vez que llegamos a la implementación).