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).
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>::iteratorconst 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.
"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?
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.
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.
(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.
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.
Respostas:
const_iterator
s não permitem alterar os valores para os quais apontam,iterator
s 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ãoconst
devem alterar o valor apontado).fonte
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:
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.
fonte
const iterater
econst_iterator
.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?
fonte
vector
edeque
, a inserção de um elemento invalida todos os iteradores existentes, o que não é muitoconst
. Mas entendo o seu ponto. Tais operações são protegidas pelo contentorconst
, 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.int const * foo;
int * const foo;
eint const * const foo;
todos os três são válidos e úteis, cada um à sua maneira.std::vector<int> const bar
deve ser o mesmo que o segundo, mas infelizmente é frequentemente tratado como o terceiro. A causa raiz do problema é que não podemos dizerstd::vector<int const> bar;
quando significa que não há como obter o mesmo efeito queint const *foo;
em um vetor.Use const_iterator sempre que puder, use iterator quando não tiver outra escolha.
fonte
Exemplos mínimos executáveis
Os iteradores não constantes permitem modificar o que eles apontam:
Const iteradores não:
Como mostrado acima,
v.begin()
estáconst
sobrecarregado e retorna umaiterator
ouconst_iterator
dependendo da constância da variável do contêiner:Um caso comum em que
const_iterator
aparece é quandothis
é usado dentro de umconst
método:const
fazthis
const, o que fazthis->v
const.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:
Qual usar: análogo ao
const int
vsint
: 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.fonte
(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.
fonte
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"
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.
fonte