c++ - ¿Std:: vector:: swap invalida los iteradores?
visual-c++ stl (4)
Si cambio dos vectores, ¿sus iteradores seguirán siendo válidos, ahora solo apuntando al "otro" contenedor, o el iterador será invalidado?
Es decir, dado:
using namespace std;
vector<int> x(42, 42);
vector<int> y;
vector<int>::iterator a = x.begin();
vector<int>::iterator b = x.end();
x.swap(y);
// a and b still valid? Pointing to x or y?
Parece que el estándar no menciona nada sobre esto:
[n3092 - 23.3.6.2]
void swap(vector<T,Allocator>& x);
Efectos: intercambia los contenidos y la capacidad () de * esto con la de x.
Tenga en cuenta que, dado que estoy en VS 2005, también me interesan los efectos de las comprobaciones de depuración de los iteradores, etc. (_SECURE_SCL)
El comportamiento del intercambio se ha aclarado considerablemente en C ++ 11, en gran parte para permitir que los algoritmos de la Biblioteca estándar utilicen la búsqueda dependiente de los argumentos (ADL) para encontrar funciones de intercambio para los tipos definidos por el usuario. C ++ 11 agrega un concepto intercambiable (C ++ 11 §17.6.3.2 [swappable.requirements]) para que esto sea legal (y obligatorio).
El texto en el estándar de lenguaje C ++ 11 que responde a su pregunta es el siguiente texto de los requisitos de contenedor (§23.2.1 [container.requirements.general] / 8), que define el comportamiento de la función de miembro de swap
de un contenedor :
Cada iterador que hace referencia a un elemento en un contenedor antes del intercambio debe hacer referencia al mismo elemento en el otro contenedor después del intercambio.
No se especifica si un iterador con el valor
a.end()
antes del swap tendrá el valorb.end()
después del swap.
En su ejemplo, a
se garantiza que será válido después del intercambio, pero b
no se debe a que sea un iterador final. La razón por la cual no se garantiza que los iteradores finales sean válidos se explica en una nota en §23.2.1 / 10:
[Nota: el iterador
end()
no se refiere a ningún elemento, por lo que puede ser invalidado. - nota final]
Este es el mismo comportamiento que se define en C ++ 03, simplemente aclarado sustancialmente. El lenguaje original de C ++ 03 está en C ++ 03 §23.1 / 10:
ninguna función
swap()
invalida cualquier referencia, puntero o iterador que haga referencia a los elementos de los contenedores que se intercambian.
No es inmediatamente obvio en el texto original, pero la frase "a los elementos de los contenedores" es extremadamente importante, porque los iteradores de end()
no apuntan a los elementos.
El intercambio de dos vectores no invalida los iteradores, punteros y referencias a sus elementos (C ++ 03, 23.1.11).
Típicamente, el iterador contendría el conocimiento de su contenedor, y la operación de intercambio lo mantiene para un iterador dado.
En VC ++ 10, el contenedor vectorial se gestiona utilizando esta estructura en <xutility>
, por ejemplo:
struct _Container_proxy
{ // store head of iterator chain and back pointer
_Container_proxy()
: _Mycont(0), _Myfirstiter(0)
{ // construct from pointers
}
const _Container_base12 *_Mycont;
_Iterator_base12 *_Myfirstiter;
};
En cuanto a Visual Studio 2005, acabo de probarlo. Creo que siempre debería funcionar, ya que la función vector :: swap incluso contiene un paso explícito para intercambiar todo:
// vector-header
void swap(_Myt& _Right)
{ // exchange contents with _Right
if (this->_Alval == _Right._Alval)
{ // same allocator, swap control information
#if _HAS_ITERATOR_DEBUGGING
this->_Swap_all(_Right);
#endif /* _HAS_ITERATOR_DEBUGGING */
...
Los iteradores apuntan a sus elementos originales en el objeto vectorial ahora intercambiado. (Es decir, con el OP, primero señalaron los elementos en x
, después del intercambio señalaron los elementos en y
)
Tenga en cuenta que en el borrador n3092 el requisito se establece en §23.2.1 / 9:
Cada iterador que hace referencia a un elemento en un contenedor antes del intercambio debe hacer referencia al mismo elemento en el otro contenedor después del intercambio.