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ão const
devem alterar o valor apontado).
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.
const iterater
e const_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?
vector
e 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.
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 bar
deve 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.
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á const
sobrecarregado e retorna uma iterator
ou const_iterator
dependendo da constância da variável do contêiner:
Um caso comum em que const_iterator
aparece é quando this
é usado dentro de um 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
faz this
const, o que faz this->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:
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 int
vs 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.