Respuestas:
const_iterator
s no le permiten cambiar los valores a los que apuntan, iterator
sí lo hacen.
Como con todas las cosas en C ++, siempre prefiera const
, a menos que haya una buena razón para usar iteradores regulares (es decir, desea usar el hecho de que no van const
a cambiar el valor señalado).
Deberían explicarse por sí mismos. Si el iterador apunta a un elemento de tipo T, entonces const_iterator apunta a un elemento de tipo 'const T'.
Básicamente es equivalente a los tipos de puntero:
T* // A non-const iterator to a non-const element. Corresponds to std::vector<T>::iterator
T* const // A const iterator to a non-const element. Corresponds to const std::vector<T>::iterator
const T* // A non-const iterator to a const element. Corresponds to std::vector<T>::const_iterator
Un iterador constante siempre apunta al mismo elemento, por lo que el iterador en sí mismo es constante. Pero el elemento al que apunta no tiene que ser constante, por lo que el elemento al que apunta puede cambiarse. Un const_iterator es un iterador que apunta a un elemento const, por lo tanto, si bien el iterador en sí puede actualizarse (incrementado o disminuido, por ejemplo), el elemento al que apunta no se puede cambiar.
const iterater
y const_iterator
.
Desafortunadamente, muchos de los métodos para los contenedores STL toman iteradores en lugar de const_iterators como parámetros. Entonces, si tiene un const_iterator , no puede decir "insertar un elemento antes del elemento al que apunta este iterador" (en mi opinión, decir que tal cosa no es conceptualmente una violación de const). Si quiere hacerlo de todos modos, debe convertirlo en un iterador no constante usando std :: advance () o boost :: next () . P.ej. boost :: next (container.begin (), std :: distance (container.begin (), the_const_iterator_we_want_to_unconst)) . Si el contenedor es una lista std :: , el tiempo de ejecución para esa llamada será O (n) .
Por lo tanto, la regla universal para agregar constante donde sea "lógico" hacerlo es menos universal cuando se trata de contenedores STL.
Sin embargo, los contenedores de impulso toman const_iterators (por ejemplo, boost :: unordered_map :: erase ()). Entonces, cuando usas contenedores de impulso puedes ser "constante agresivo". Por cierto, ¿alguien sabe si o cuándo se repararán los contenedores STL?
vector
y deque
, la inserción de un elemento invalida todos los iteradores existentes, lo cual no es muy const
. Pero sí entiendo tu punto. Dichas operaciones están protegidas por contenedor const
-ness, no los iteradores. Y me pregunto por qué no hay una función de conversión de iterador const a nonconst en la interfaz de contenedor estándar.
int const * foo;
int * const foo;
y int const * const foo;
los tres son válidos y útiles, cada uno a su manera. std::vector<int> const bar
debería ser el mismo que el segundo, pero desafortunadamente a menudo se trata como el tercero. La causa raíz del problema es que no podemos decir std::vector<int const> bar;
cuándo significa que no hay forma de obtener el mismo efecto que int const *foo;
en un vector.
Ejemplos ejecutables mínimos
Los iteradores no constantes le permiten modificar lo que apuntan:
std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();
*it = 1;
assert(v[0] == 1);
Los iteradores constantes no:
const std::vector<int> v{0};
std::vector<int>::const_iterator cit = v.begin();
// Compile time error: cannot modify container with const_iterator.
//*cit = 1;
Como se muestra arriba, v.begin()
está const
sobrecargado y devuelve cualquiera iterator
o const_iterator
dependiendo de la constancia de la variable contenedor:
Un caso común en el que const_iterator
aparece una ventana emergente es cuando this
se usa dentro de un const
método:
class C {
public:
std::vector<int> v;
void f() const {
std::vector<int>::const_iterator it = this->v.begin();
}
void g(std::vector<int>::const_iterator& it) {}
};
const
hace this
const, que hace this->v
const.
Por lo general, puede olvidarse de ello con auto
, pero si comienza a pasar esos iteradores, deberá pensar en ellos para las firmas de métodos.
Al igual que const y no const, puede convertir fácilmente de no const a const, pero no al revés:
std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();
// non-const to const.
std::vector<int>::const_iterator cit = it;
// Compile time error: cannot modify container with const_iterator.
//*cit = 1;
// Compile time error: no conversion from const to no-const.
//it = ci1;
Cuál usar: análogo a const int
vs int
: prefiera los iteradores constantes siempre que pueda usarlos (cuando no necesite modificar el contenedor con ellos), para documentar mejor su intención de leer sin modificar.
(como han dicho otros) const_iterator no le permite modificar los elementos a los que apunta, esto es útil dentro de los métodos de clase const. También le permite expresar su intención.
ok Permítanme explicarlo con un ejemplo muy simple primero sin usar un iterador constante, consideremos que tenemos una colección de enteros aleatorios colección "randomData"
for(vector<int>::iterator i = randomData.begin() ; i != randomData.end() ; ++i)*i = 0;
for(vector<int>::const_iterator i = randomData.begin() ; i!= randomData.end() ; ++i)cout << *i;
Como se puede ver para escribir / editar datos dentro de la colección, se usa un iterador normal pero para fines de lectura se ha usado un iterador constante. Si intenta usar un iterador constante en el primer bucle, obtendrá un error. Como regla general, use un iterador constante para leer los datos dentro de la colección.