¿Cuál es la diferencia entre const_iterator y no-const iterator en C ++ STL?

139

¿Cuál es la diferencia entre a const_iteratory an iteratory dónde usaría uno sobre el otro?

Konrad
fuente
1
Bueno, el nombre const_iterator suena como que el iterador es const, mientras que lo que señala este iterador es el const real.
talekeDskobeDa

Respuestas:

124

const_iterators no le permiten cambiar los valores a los que apuntan, iteratorsí 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 consta cambiar el valor señalado).

Dominic Rodger
fuente
8
En un mundo perfecto ese sería el caso. Pero con C ++ const es tan bueno como la persona que escribió el código :(
JaredPar
mutable existe por una muy buena razón. Raramente se usa, y al mirar Google Code Search, parece haber un porcentaje justo de usos válidos. La palabra clave es una herramienta de optimización muy poderosa, y no es como eliminarla mejoraría la corrección constante ( coughpointerscoughandcoughreferencescough )
coppro
2
Más como un truco poderoso. De todas las instancias que he visto del uso de la palabra clave 'mutable', todas menos una fueron un indicador preciso de que el código estaba mal escrito y que mutable era necesario como un truco para sortear los defectos.
John Dibling el
77
Tiene usos legítimos, como el almacenamiento en caché de los resultados de un cálculo largo dentro de una clase constante. Por otro lado, esa es la única vez que he usado un mutable en casi veinte años de desarrollo de C ++.
Head Geek el
Un ejemplo genérico para usar const_iterator sería cuando el iterador es un valor r
talekeDskobeDa
40

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.

jalf
fuente
1
"Un iterador constante siempre apunta al mismo elemento," Esto es incorrecto.
John Dibling el
2
¿Cómo es eso? Tenga en cuenta el guión bajo que falta. Estoy contrastando una variable de tipo const std :: vector <T> :: iterator con std :: vector <T> :: const_iterator. En el primer caso, el iterador en sí mismo es constante, por lo que no se puede modificar, pero el elemento al que hace referencia se puede modificar libremente.
jalf
44
Ah, ya veo. Sí, me perdí el guión bajo perdido.
John Dibling
3
@JohnDibling Upvoted por explicar la sutileza entre const iteratery const_iterator.
legends2k
8

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?

Magnus Andermo
fuente
1
Puede ser una cuestión de opinión. En el caso de vectory 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.
Potatoswatter
Tienes razón Potatoswatter, soy categórico, es una cuestión de opinión para los contenedores de acceso aleatorio y container.begin () + (the_const_iterator_we_want_to_unconst - container.begin ()) es O (1) de todos modos. También me pregunto por qué no hay una función de conversión para los contenedores de acceso no aleatorio, pero ¿tal vez hay una buena razón? ¿Sabes si hay alguna razón por la cual las funciones para los contenedores de acceso no aleatorio no toman const_iterators ?
Magnus Andermo
"decir que tal cosa no es conceptualmente una violación constante, en mi opinión" - este es un comentario muy interesante, tengo las siguientes ideas sobre el tema. Con punteros simples, uno puede decir 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 bardeberí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.
dgnuff
5

Use const_iterator siempre que pueda, use iterator cuando no tenga otra opción.

Naveen
fuente
4

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á constsobrecargado y devuelve cualquiera iteratoro const_iteratordependiendo de la constancia de la variable contenedor:

Un caso común en el que const_iteratoraparece una ventana emergente es cuando thisse usa dentro de un constmé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) {}
};

consthace thisconst, que hace this->vconst.

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 intvs 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.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
0

(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.

Trey Jackson
fuente
0

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.

Sr. codificador
fuente