Qual é a diferença entre const_iterator e non-const iterator no C ++ STL?

139

Qual é a diferença entre a const_iteratore an iteratore onde você usaria um sobre o outro?

Konrad
fonte
1
Bem, o nome const_iterator soa como o iterador é const, enquanto o que esse iterador aponta é a const real.
talekeDskobeDa

Respostas:

124

const_iterators não permitem alterar os valores para os quais apontam, iterators regulares .

Como em todas as coisas em C ++, sempre prefira const, a menos que haja um bom motivo para usar iteradores regulares (ou seja, você deseja usar o fato de que eles não constdevem alterar o valor apontado).

Dominic Rodger
fonte
8
Em um mundo perfeito, esse seria o caso. Mas com C ++ const é tão bom quanto a pessoa que escreveu o código :(
JaredPar
mutável existe por um motivo muito bom. É raramente usado e, olhando para a Pesquisa de código do Google, parece haver uma porcentagem justa de usos válidos. A palavra-chave é uma ferramenta de otimização muito poderoso, e não é como a remoção seria melhorar const-correção ( coughpointerscoughandcoughreferencescough )
coppro
2
Mais como um hack poderoso. De todos os casos que eu já vi sobre o uso da palavra-chave 'mutable', todos, exceto um, foram um indicador preciso de que o código estava mal escrito e que o mutable era necessário como um truque para contornar os defeitos.
John Dibling
7
Ele tem usos legítimos, como armazenar em cache os resultados de um cálculo longo dentro de uma classe const. Por outro lado, essa é a única vez que usei um mutável em quase vinte anos de desenvolvimento em C ++.
Head Geek
Um exemplo genérico para usar const_iterator seria quando o iterador é um rvalue
talekeDskobeDa
40

Eles devem ser praticamente auto-explicativos. Se o iterador apontar para um elemento do tipo T, const_iterator apontará para um elemento do tipo 'const T'.

É basicamente equivalente aos tipos de ponteiro:

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

Um iterador const sempre aponta para o mesmo elemento, portanto, o iterador em si é const. Mas o elemento para o qual ele aponta não precisa ser const; portanto, o elemento para o qual ele aponta pode ser alterado. Um const_iterator é um iterador que aponta para um elemento const; portanto, enquanto o próprio iterador pode ser atualizado (incrementado ou decrementado, por exemplo), o elemento para o qual ele aponta não pode ser alterado.

jalf
fonte
1
"Um iterador constante sempre aponta para o mesmo elemento", isto está incorreto.
John Dibling
2
Como assim? Observe o sublinhado ausente. Estou contrastando uma variável do tipo const std :: vector <T> :: iterator com std :: vector <T> :: const_iterator. No primeiro caso, o iterador em si é const, portanto não pode ser modificado, mas o elemento ao qual ele faz referência pode ser modificado livremente.
jalf
4
Ah entendo. Sim, eu perdi o sublinhado que faltava.
John Dibling 22/11/2008
3
@JohnDibling Voto a favor por explicar a sutileza entre const iteratere const_iterator.
precisa saber é o seguinte
8

Infelizmente, muitos dos métodos para os contêineres STL usam iteradores em vez de const_iterators como parâmetros. Portanto, se você possui um const_iterator , não pode dizer "insira um elemento antes do elemento para o qual este iterador aponta" (dizer que tal coisa não é conceitualmente uma violação const, na minha opinião). Se você quiser fazer isso de qualquer maneira, precisará convertê-lo em um iterador não-const usando std :: advance () ou boost :: next () . Por exemplo. boost :: next (container.begin (), std :: distance (container.begin (), the_const_iterator_we_want_to_unconst)) . Se o contêiner for uma lista std :: , o tempo de execução dessa chamada será O (n) .

Portanto, a regra universal para adicionar const, sempre que for "lógico", é menos universal quando se trata de contêineres STL.

No entanto, os contêineres boost recebem const_iterators (por exemplo, boost :: unordered_map :: erase ()). Então, quando você usa contêineres de reforço, pode ser "constante agressivo". A propósito, alguém sabe se ou quando os contêineres da STL serão corrigidos?

Magnus Andermo
fonte
1
Pode ser uma questão de opinião. No caso de vectore deque, a inserção de um elemento invalida todos os iteradores existentes, o que não é muito const. Mas entendo o seu ponto. Tais operações são protegidas pelo contentor const, e não pelos iteradores. E me pergunto por que não há uma função de conversão de iterador de constante para não-constante na interface padrão do contêiner.
Potatoswatter
Você está correto em Potatoswatter, eu sou categórico, é uma questão de opinião para os contêineres de acesso aleatório e container.begin () + (the_const_iterator_we_want_to_unconst - container.begin ()) é O (1) de qualquer maneira. Também me pergunto por que não há função de conversão para os contêineres de acesso não aleatório, mas talvez haja uma boa razão? Você sabe se existe algum motivo para as funções dos contêineres de acesso não aleatório não usarem const_iterators ?
Magnus Andermo
"dizer que tal coisa não é conceitualmente uma violação const, na minha opinião" - este é um comentário muito interessante, tenho os seguintes pensamentos sobre o assunto. Com indicadores simples, pode-se dizer int const * foo; int * const foo;e int const * const foo;todos os três são válidos e úteis, cada um à sua maneira. std::vector<int> const bardeve ser o mesmo que o segundo, mas infelizmente é frequentemente tratado como o terceiro. A causa raiz do problema é que não podemos dizer std::vector<int const> bar;quando significa que não há como obter o mesmo efeito que int const *foo;em um vetor.
dgnuff
5

Use const_iterator sempre que puder, use iterator quando não tiver outra escolha.

Naveen
fonte
4

Exemplos mínimos executáveis

Os iteradores não constantes permitem modificar o que eles apontam:

std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();
*it = 1;
assert(v[0] == 1);

Const iteradores não:

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 mostrado acima, v.begin()está constsobrecarregado e retorna uma iteratorou const_iteratordependendo da constância da variável do contêiner:

Um caso comum em que const_iteratoraparece é quando thisé usado dentro de um 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) {}
};

constfaz thisconst, o que faz this->vconst.

Geralmente, você pode esquecê-lo com isso auto, mas se você começar a repassar esses iteradores, precisará pensar neles para obter as assinaturas do método.

Assim como const e non-const, você pode converter facilmente de não-const para const, mas não o contrário:

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;

Qual usar: análogo ao const intvs int: prefira os iteradores constantes sempre que você puder usá-los (quando não precisar modificar o contêiner com eles), para documentar melhor sua intenção de ler sem modificar.

Ciro Santilli adicionou uma nova foto
fonte
0

(como outros já disseram) const_iterator não permite modificar os elementos para os quais aponta, isso é útil dentro dos métodos da classe const. Também permite que você expresse sua intenção.

Trey Jackson
fonte
0

ok Deixe-me explicar primeiro com um exemplo muito simples, sem usar o iterador constante, considerando que temos uma coleção de números inteiros aleatórios "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 pode ser visto para gravar / editar dados dentro da coleção, o iterador normal é usado, mas para fins de leitura, o iterador constante foi usado. Se você tentar usar o iterador constante primeiro no loop for, receberá um erro. Como regra geral, use o iterador constante para ler os dados dentro da coleção.

Mr Coder
fonte