Gostaria de saber por que cbegin
e cend
foram introduzidos no C ++ 11?
Quais são os casos em que chamar esses métodos faz diferença das sobrecargas const de begin
e end
?
Gostaria de saber por que cbegin
e cend
foram introduzidos no C ++ 11?
Quais são os casos em que chamar esses métodos faz diferença das sobrecargas const de begin
e end
?
É 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 questd::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/beginfor(auto &item : std::as_const(vec))
equivalente afor(const auto &item : vec)
?const
na referência. Nicol vê o contêiner como const, portantoauto
deduz umaconst
referência. OMIauto const& item
é mais fácil e mais claro. Não está claro por questd::as_const()
é bom aqui; Percebo que seria útil passar algo nãoconst
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:Com
auto
, não há como garantir quebegin()
retorne um operador constante para uma referência de contêiner não constante. Então agora você faz:fonte
const_iterator
é apenas outro identificador. Nenhuma versão usa uma pesquisa dos membros usuais typedefsdecltype(container)::iterator
oudecltype(container)::const_iterator
.const_iterator
comauto
: Escrever um modelo de função auxiliar chamadamake_const
para qualificar o argumento de objeto.Tome isso como um caso prático
A atribuição falha porque
it
é um iterador não constante. Se você usou cbegin inicialmente, o iterador teria o tipo certo.fonte
De http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1674.pdf :
Eles deram este exemplo
Note-se que o documento de trabalho também menciona adaptador modelos, que agora foram finalizadas como
std::begin()
estd::end()
e que também trabalham com matrizes nativas. Os correspondentesstd::cbegin()
estd::cend()
estão curiosamente ausentes a partir deste momento, mas eles também podem ser adicionados.fonte
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ãoauto 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 parastd::cbegin()
estd::cend()
, que é essencialmente o que se deve usar quando estiver aqui) ...fonte
iterator
econst_iterator
ter um relacionamento de herança e ocorre uma conversão implícita quando comparada com ou atribuída ao outro tipo.Usando
cbegin()
ecend()
aumentará o desempenho neste caso.fonte
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 oend
iterador declarando uma cópia dele na condição init dofor
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. : Pconst
pode definitivamente ajudar a obter um melhor desempenho, não por causa de alguma mágica naconst
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 adicionarconst
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 :
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:
isso não vai funcionar, porque você não pode atualizar o valor usando cbegin e cend, que retorna o iterador constante
fonte