Respostas:
É bem simples Digamos que eu tenha um vetor:
std::vector<int> vec;
Eu preencho com alguns dados. Então eu quero obter alguns iteradores para ele. Talvez passá-los por aí. Talvez para std::for_each
:
std::for_each(vec.begin(), vec.end(), SomeFunctor());
No C ++ 03, SomeFunctor
estava livre para poder modificar o parâmetro obtido. Claro, SomeFunctor
poderia pegar seu parâmetro por valor ou por const&
, mas não há como garantir isso. Não sem fazer algo bobo como este:
const std::vector<int> &vec_ref = vec;
std::for_each(vec_ref.begin(), vec_ref.end(), SomeFunctor());
Agora, apresentamos cbegin/cend
:
std::for_each(vec.cbegin(), vec.cend(), SomeFunctor());
Agora, temos garantias sintáticas que SomeFunctor
não podem modificar os elementos do vetor (sem const-cast, é claro). Nós obtemos explicitamente const_iterator
s e, portanto SomeFunctor::operator()
, seremos chamados com const int &
. Se for necessário como parâmetros int &
, o C ++ emitirá um erro do compilador.
C ++ 17 tem uma solução mais elegante para este problema: std::as_const
. Bem, pelo menos é elegante ao usar o intervalo for
:
for(auto &item : std::as_const(vec))
Isso simplesmente retorna const&
a ao objeto que é fornecido.
std::cbegin/cend
funções gratuitas da maneira que std::begin/std::end
existem. Foi uma supervisão do comitê. Se essas funções existissem, essa seria geralmente a maneira de usá-las.
std::cbegin/cend
será adicionado no C ++ 14. Veja en.cppreference.com/w/cpp/iterator/begin
for(auto &item : std::as_const(vec))
equivalente a for(const auto &item : vec)
?
const
na referência. Nicol vê o contêiner como const, portanto auto
deduz uma const
referência. OMI auto const& item
é mais fácil e mais claro. Não está claro por que std::as_const()
é bom aqui; Percebo que seria útil passar algo não const
genérico em código onde não podemos controlar o tipo que é usado, mas com range- for
, podemos, por isso parece um ruído adicional para mim.
Além do que Nicol Bolas disse em sua resposta , considere a nova auto
palavra-chave:
auto iterator = container.begin();
Com auto
, não há como garantir que begin()
retorne um operador constante para uma referência de contêiner não constante. Então agora você faz:
auto const_iterator = container.cbegin();
const_iterator
é apenas outro identificador. Nenhuma versão usa uma pesquisa dos membros usuais typedefs decltype(container)::iterator
ou decltype(container)::const_iterator
.
const_iterator
com auto
: Escrever um modelo de função auxiliar chamada make_const
para qualificar o argumento de objeto.
Tome isso como um caso prático
void SomeClass::f(const vector<int>& a) {
auto it = someNonConstMemberVector.begin();
...
it = a.begin();
...
}
A atribuição falha porque it
é um iterador não constante. Se você usou cbegin inicialmente, o iterador teria o tipo certo.
De http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1674.pdf :
para que um programador possa obter diretamente um const_iterator mesmo de um contêiner não-const
Eles deram este exemplo
vector<MyType> v;
// fill v ...
typedef vector<MyType>::iterator iter;
for( iter it = v.begin(); it != v.end(); ++it ) {
// use *it ...
}
No entanto, quando uma travessia de contêiner é destinada apenas à inspeção, é geralmente uma prática preferida usar um const_iterator para permitir que o compilador diagnostique violações da correção da const
Note-se que o documento de trabalho também menciona adaptador modelos, que agora foram finalizadas como std::begin()
e std::end()
e que também trabalham com matrizes nativas. Os correspondentes std::cbegin()
e std::cend()
estão curiosamente ausentes a partir deste momento, mas eles também podem ser adicionados.
Só me deparei com esta pergunta ... Eu sei que já está respondida e é apenas um nó lateral ...
auto const it = container.begin()
é um tipo diferente então auto it = container.cbegin()
A diferença para int[5]
(usando ponteiro, que eu sei que não tem o método begin, mas mostra bem a diferença ... mas funcionaria em c ++ 14 para std::cbegin()
e std::cend()
, que é essencialmente o que se deve usar quando estiver aqui) ...
int numbers = array[7];
const auto it = begin(numbers); // type is int* const -> pointer is const
auto it = cbegin(numbers); // type is int const* -> value is const
iterator
e const_iterator
ter um relacionamento de herança e ocorre uma conversão implícita quando comparada com ou atribuída ao outro tipo.
class T {} MyT1, MyT2, MyT3;
std::vector<T> MyVector = {MyT1, MyT2, MyT3};
for (std::vector<T>::const_iterator it=MyVector.begin(); it!=MyVector.end(); ++it)
{
// ...
}
Usando cbegin()
e cend()
aumentará o desempenho neste caso.
for (std::vector<T>::const_iterator it=MyVector.cbegin(); it!=MyVector.cend(); ++it)
{
// ...
}
const
o principal benefício é o desempenho (o que não é: é um código semanticamente correto e seguro). Mas, enquanto você tem um argumento, (A) auto
torna isso um problema; (B) ao falar sobre desempenho, você perdeu uma das coisas principais que deveria ter feito aqui: armazenar em cache o end
iterador declarando uma cópia dele na condição init do for
loop, e comparar com isso, em vez de obter uma nova cópia valor para cada iteração. Isso fará com que o seu ponto seja melhor. : P
const
pode definitivamente ajudar a obter um melhor desempenho, não por causa de alguma mágica na const
própria palavra-chave, mas porque o compilador pode ativar algumas otimizações se souber que os dados não serão modificados, o que não seria possível de outra forma. Confira este trecho da palestra de Jason Turner para um exemplo ao vivo disso.
const
pode (quase indiretamente) levar a benefícios de desempenho; apenas no caso de alguém que esteja lendo isso possa pensar "Não vou me incomodar em adicionar const
se o código gerado não for afetado de forma alguma" ", o que não é verdade.
simples, cbegin retorna um iterador constante, onde begin retorna apenas um iterador
para uma melhor compreensão, vamos dar dois cenários aqui
Cenário 1 :
#include <iostream>
using namespace std;
#include <vector>
int main(int argc, char const *argv[])
{
std::vector<int> v;
for (int i = 1; i < 6; ++i)
{
/* code */
v.push_back(i);
}
for(auto i = v.begin();i< v.end();i++){
*i = *i + 5;
}
for (auto i = v.begin();i < v.end();i++){
cout<<*i<<" ";
}
return 0;
}
isso será executado porque aqui o iterador i não é constante e pode ser incrementado em 5
agora vamos usar cbegin e cend denotando-os como cenário de iteradores constantes - 2:
#include <iostream>
using namespace std;
#include <vector>
int main(int argc, char const *argv[])
{
std::vector<int> v;
for (int i = 1; i < 6; ++i)
{
/* code */
v.push_back(i);
}
for(auto i = v.cbegin();i< v.cend();i++){
*i = *i + 5;
}
for (auto i = v.begin();i < v.end();i++){
cout<<*i<<" ";
}
return 0;
}
isso não vai funcionar, porque você não pode atualizar o valor usando cbegin e cend, que retorna o iterador constante