Existem vantagens do std::for_each
over for
loop? Para mim, std::for_each
apenas parece dificultar a legibilidade do código. Por que alguns padrões de codificação recomendam seu uso?
c++
stl
foreach
coding-style
desaparecido
fonte
fonte
std::for_each
quando usado comboost.lambda
ouboost.bind
pode melhorar a legibilidadeRespostas:
O interessante do C ++ 11 (anteriormente chamado C ++ 0x) é que esse debate cansativo será resolvido.
Quero dizer, ninguém em sã consciência, que deseja iterar uma coleção inteira, ainda usará isso
Ou isto
quando a sintaxe do loop baseado em intervalo
for
estiver disponível:Esse tipo de sintaxe está disponível em Java e C # há algum tempo e, na verdade, existem muito mais
foreach
loops quefor
loops clássicos em todos os códigos Java ou C # recentes que vi.fonte
Element & e
queauto & e
(ouauto const &e
) pareça melhor. Eu usariaElement const e
(sem referência) quando quiser uma conversão implícita, digamos quando a fonte for uma coleção de tipos diferentes e quero que eles se convertamElement
.Aqui estão alguns motivos:
Parece dificultar a legibilidade apenas porque você não está acostumado a isso e / ou não está usando as ferramentas certas para torná-lo realmente fácil. (consulte boost :: range e boost :: bind / boost :: lambda para auxiliares. Muitos deles vão para o C ++ 0x e tornam o for_each e as funções relacionadas mais úteis.)
Ele permite que você escreva um algoritmo sobre for_each que funcione com qualquer iterador.
Reduz a chance de erros estúpidos de digitação.
Ele também abre a sua mente para o resto dos STL-algoritmos, como
find_if
,sort
,replace
, etc e estes não vão olhar mais tão estranho. Isso pode ser uma grande vitória.Atualização 1:
Mais importante, ele ajuda você a ir além dos
for_each
x for-loops, como é tudo o que existe, e olhar para os outros STL-alogs, como find / sort / partition / copy_replace_if, execução paralela .. ou o que for.Muito processamento pode ser escrito de forma muito concisa usando "o resto" dos irmãos de for_each, mas se tudo o que você faz é escrever um loop for com várias lógicas internas, nunca aprenderá como usá-las e acabam inventando a roda repetidamente.
E (em breve disponível para cada estilo de intervalo):
Ou com lambdas C ++ x11:
é IMO mais legível do que:
Também isso (ou com lambdas, veja outros):
É mais conciso do que:
Especialmente se você tiver várias funções para chamar em ordem ... mas talvez seja apenas eu. ;)
Atualização 2 : eu escrevi meus próprios invólucros de uma linha de stl-algos que funcionam com intervalos em vez de pares de iteradores. O boost :: range_ex, uma vez lançado, incluirá isso e talvez também esteja lá no C ++ 0x?
fonte
outer_class::inner_class::iterator
ou são argumentos de modelo:typename std::vector<T>::iterator
... o construtor for em si pode executar em si mesmo um construtor de muitas linhasfor_each
no segundo exemplo está incorreto (deve serfor_each( bananas.begin(), bananas.end(),...
for_each
é mais genérico. Você pode usá-lo para iterar sobre qualquer tipo de contêiner (passando os iteradores de início / fim). Você pode trocar os contêineres potencialmente por baixo de uma função usadafor_each
sem precisar atualizar o código de iteração. Você precisa considerar que existem outros contêineres no mundo além destd::vector
matrizes C simples para ver as vantagens defor_each
.A principal desvantagem
for_each
é que é preciso um functor, portanto a sintaxe é desajeitada. Isso foi corrigido no C ++ 11 (anteriormente C ++ 0x) com a introdução de lambdas:Isso não parecerá estranho para você em três anos.
fonte
for ( int v : int_vector ) {
(mesmo que pode ser simulado hoje com BOOST_FOREACH)std::for_each(container, [](int& i){ ... });
. Quero dizer, por que alguém é forçado a escrever contêiner duas vezes?container.each { ... }
sem mencionar os iteradores de início e fim. Acho um pouco redundante precisar especificar o iterador final o tempo todo.Pessoalmente, sempre que eu precisar sair do meu caminho para usar
std::for_each
(escreva functors para fins especiais /boost::lambda
s complicados ), acho que oBOOST_FOREACH
C ++ 0x é baseado em intervalo para maior clareza:vs
fonte
é muito subjetivo, alguns dirão que o uso
for_each
tornará o código mais legível, pois permite tratar diferentes coleções com as mesmas convenções.for_each
itslef é implementado como um loopentão cabe a você escolher o que é certo para você.
fonte
Como muitas das funções do algoritmo, uma reação inicial é pensar que é mais ilegível usar foreach do que um loop. Tem sido um tópico de muitas guerras de chamas.
Depois de se acostumar com o idioma, você pode achar útil. Uma vantagem óbvia é que ele força o codificador a separar o conteúdo interno do loop da funcionalidade de iteração real. (OK, acho que é uma vantagem. Outros dizem que você está apenas cortando o código sem nenhum benefício real).
Uma outra vantagem é que, quando vejo foreach, sei que todos os itens serão processados ou uma exceção será lançada.
Um loop for permite várias opções para finalizar o loop. Você pode deixar o loop executar seu curso completo ou usar a palavra-chave break para sair explicitamente do loop ou usar a palavra-chave return para sair de toda a função no meio do loop. Por outro lado, o foreach não permite essas opções, e isso a torna mais legível. Você pode apenas olhar o nome da função e conhecer a natureza completa da iteração.
Aqui está um exemplo de um loop for confuso :
fonte
std::for_each()
o padrão antigo (o da época desta postagem), é necessário usar um functor nomeado, o que incentiva a legibilidade, como você diz, e proíbe interromper prematuramente o ciclo. Mas ofor
loop equivalente não tem mais que uma chamada de função, e isso também proíbe a interrupção prematura. Mas, além disso, acho que você fez um excelente argumento ao dizer questd::for_each()
reforça o alcance de toda a gama.Você está mais correto: na maioria das vezes,
std::for_each
é uma perda líquida. Eu iria tão longe para compararfor_each
agoto
.goto
fornece o controle de fluxo mais versátil possível - você pode usá-lo para implementar praticamente qualquer outra estrutura de controle que você possa imaginar. Essa versatilidade, no entanto, significa que ver umgoto
isoladamente não diz praticamente nada sobre o que se pretende fazer nessa situação. Como resultado, quase ninguém em sã consciência usa,goto
exceto como último recurso.Entre os algoritmos padrão,
for_each
é praticamente o mesmo - ele pode ser usado para implementar praticamente qualquer coisa, o que significa que ver nãofor_each
diz praticamente nada sobre o que está sendo usado nessa situação. Infelizmente, a atitude das pessoas em relação ao localfor_each
é onde elasgoto
estavam (digamos) em 1970, aproximadamente - algumas pessoas perceberam que ela deveria ser usada apenas como último recurso, mas muitas ainda a consideram o algoritmo primário, e raramente, se alguma vez, usar outro. Na grande maioria das vezes, mesmo um rápido olhar revelaria que uma das alternativas era drasticamente superior.Apenas por exemplo, tenho certeza de que perdi o controle de quantas vezes vi pessoas escrevendo código para imprimir o conteúdo de uma coleção usando
for_each
. Com base nas postagens que eu já vi, esse pode ser o uso mais comumfor_each
. Eles acabam com algo como:E seu posto está perguntando sobre qual combinação de
bind1st
,mem_fun
, etc. Eles precisam fazer algo como:trabalho e imprima os elementos de
coll
. Se realmente funcionou exatamente como eu escrevi lá, seria medíocre, mas não funciona - e quando você o faz funcionar, é difícil encontrar esses poucos bits de código relacionados ao que é acontecendo entre as peças que a mantêm unida.Felizmente, existe uma maneira muito melhor. Adicione uma sobrecarga normal do inserdor de fluxo para XXX:
e use
std::copy
:Isso funciona - e praticamente não exige trabalho algum para descobrir que ele imprime o conteúdo de
coll
parastd::cout
.fonte
boost::mem_fn(&XXX::print)
melhor queXXX::print
std::cout
como argumento para que funcione).A vantagem de escrever funcional para ser mais legível pode não aparecer quando
for(...)
efor_each(...
).Se você utiliza todos os algoritmos em functional.h, em vez de usar loops for, o código fica muito mais legível;
é muito mais legível do que;
E é isso que eu acho tão legal: generalize os for-loops para as funções de uma linha =)
fonte
Fácil:
for_each
é útil quando você já possui uma função para lidar com todos os itens da matriz, para que não precise escrever uma lambda. Certamente, issoé melhor que
Além disso, o
for
loop à distância itera apenas contêineres inteiros do início ao fim, enquantofor_each
é mais flexível.fonte
O
for_each
loop visa ocultar os iteradores (detalhes de como um loop é implementado) do código do usuário e definir semânticas claras na operação: cada elemento será iterado exatamente uma vez.O problema com a legibilidade no padrão atual é que ele requer um functor como o último argumento em vez de um bloco de código; portanto, em muitos casos, você deve escrever um tipo específico de functor para ele. Isso se transforma em código menos legível, pois os objetos functor não podem ser definidos no local (classes locais definidas em uma função não podem ser usadas como argumentos de modelo) e a implementação do loop deve ser afastada do loop real.
Observe que, se você deseja executar uma operação específica em cada objeto, pode usar
std::mem_fn
, ouboost::bind
(std::bind
no próximo padrão) ouboost::lambda
(lambdas no próximo padrão) para simplificá-lo:O que não é menos legível e mais compacto do que a versão manual, se você tiver uma função / método para chamar. A implementação pode fornecer outras implementações do
for_each
loop (pense em processamento paralelo).O próximo padrão trata de algumas das deficiências de maneiras diferentes, permitindo classes definidas localmente como argumentos para modelos:
Melhorando a localidade do código: quando você navega, vê o que está fazendo ali. Por uma questão de fato, você nem precisa usar a sintaxe da classe para definir o functor, mas use um lambda ali:
Mesmo que, no caso de,
for_each
exista uma construção específica que a torne mais natural:Costumo misturar a
for_each
construção com loops enrolados à mão. Quando apenas uma chamada para uma função ou método existente é o que eu preciso (for_each( v.begin(), v.end(), boost::bind( &Type::update, _1 ) )
), busco afor_each
construção que retira do código muitas coisas do iterador da placa da caldeira. Quando preciso de algo mais complexo e não consigo implementar um functor apenas algumas linhas acima do uso real, rolo meu próprio loop (mantém a operação no lugar). Em seções não críticas do código, posso usar o BOOST_FOREACH (um colega de trabalho me envolveu)fonte
Além da legibilidade e desempenho, um aspecto geralmente esquecido é a consistência. Existem várias maneiras de implementar um loop for (ou while) nos iteradores, a partir de:
para:
com muitos exemplos entre os diferentes níveis de eficiência e potencial de erros.
O uso de for_each, no entanto, reforça a consistência abstraindo o loop:
A única coisa com a qual você precisa se preocupar agora é: você implementa o corpo do loop como função, um functor ou um lambda usando os recursos Boost ou C ++ 0x? Pessoalmente, prefiro me preocupar com isso do que como implementar ou ler um loop for / while aleatório.
fonte
Eu não gostava
std::for_each
e pensava que, sem o lambda, isso era totalmente errado. No entanto, mudei de idéia há algum tempo, e agora eu realmente amo isso. E acho que melhora a legibilidade e facilita o teste do seu código de forma TDD.O
std::for_each
algoritmo pode ser lido como fazer algo com todos os elementos no intervalo , o que pode melhorar a legibilidade. Digamos que a ação que você deseja executar tenha 20 linhas e a função em que a ação é realizada também tenha cerca de 20 linhas. Isso tornaria uma função de 40 linhas com um loop for convencional e apenas cerca de 20 comstd::for_each
, portanto, provavelmente mais fácil de compreender.É
std::for_each
mais provável que os funcores sejam mais genéricos e, portanto, reutilizáveis, por exemplo:E no código, você teria apenas uma linha como
std::for_each(v.begin(), v.end(), DeleteElement())
IMO ligeiramente melhor que um loop explícito.Todos esses functores são normalmente mais fáceis de serem submetidos a testes de unidade do que um loop for explícito no meio de uma função longa, e isso por si só já é uma grande vitória para mim.
std::for_each
também é geralmente mais confiável, pois é menos provável que você cometa um erro de alcance.E, por fim, o compilador pode produzir um código um pouco melhor para o
std::for_each
que para certos tipos de loop for criados manualmente, pois (for_each) sempre parece o mesmo para o compilador, e os escritores do compilador podem colocar todo o seu conhecimento para torná-lo tão bom quanto possível. pode.Mesmo se aplica a outros algoritmos de DST como
find_if
,transform
etc.fonte
for
é para loop que pode iterar cada elemento ou cada terceiro etc.for_each
é para iterar apenas cada elemento. Está claro pelo nome. Portanto, fica mais claro o que você pretende fazer no seu código.fonte
++
. Incomum talvez, mas o loop for também está fazendo o mesmo.transform
para não confundir alguém.Se você usa frequentemente outros algoritmos do STL, há várias vantagens em
for_each
:Ao contrário de um loop for tradicional,
for_each
obriga a escrever um código que funcione para qualquer iterador de entrada. Restringir dessa maneira pode ser uma coisa boa porque:for_each
.for_each
Às vezes, o uso torna mais óbvio que você pode usar uma função STL mais específica para fazer a mesma coisa. (Como no exemplo de Jerry Coffin; não é necessariamente o caso quefor_each
é a melhor opção, mas um loop for não é a única alternativa.)fonte
Com o C ++ 11 e dois modelos simples, você pode escrever
como um substituto para
for_each
ou um loop. Por que a escolha se resume a brevidade e segurança, não há chance de erro em uma expressão que não existe.Para mim,
for_each
sempre foi melhor pelo mesmo motivo quando o corpo do loop já é um functor e aproveitarei qualquer vantagem que puder obter.Você ainda usa as três expressões
for
, mas agora, quando você vê uma que sabe que há algo para entender, não é uma clichê. Eu odeio clichê. Eu me ressinto de sua existência. Não é um código real, não há nada a aprender lendo-o, é apenas mais uma coisa que precisa ser verificada. O esforço mental pode ser medido pelo quão fácil é ficar enferrujado ao checá-lo.Os modelos são
fonte
Principalmente, você terá que percorrer toda a coleção . Portanto, sugiro que você escreva sua própria variante for_each (), usando apenas 2 parâmetros. Isso permitirá que você reescreva o exemplo de Terry Mahaffey como:
Eu acho que isso é realmente mais legível do que um loop for. No entanto, isso requer as extensões do compilador C ++ 0x.
fonte
Acho for_each ruim para facilitar a leitura. O conceito é bom, mas o c ++ torna muito difícil escrever legível, pelo menos para mim. As expressões c ++ 0x lamda ajudarão. Eu realmente gosto da idéia de lamdas. No entanto, à primeira vista, acho que a sintaxe é muito feia e não tenho 100% de certeza de que vou me acostumar. Talvez em cinco anos eu me acostumei e não pense duas vezes, mas talvez não. O tempo vai dizer :)
Eu prefiro usar
Acho que um loop for explícito mais claro para ler e explicitamente, usando variáveis nomeadas para os iteradores de início e fim, reduz a desordem no loop for.
É claro que os casos variam, é exatamente o que eu costumo achar melhor.
fonte
Você pode fazer com que o iterador seja uma chamada para uma função que é executada em cada iteração pelo loop.
Veja aqui: http://www.cplusplus.com/reference/algorithm/for_each/
fonte
for_each
que, nesse caso, não responde à pergunta sobre suas vantagens.For loop pode quebrar; Eu não quero ser um papagaio para Herb Sutter, então aqui está o link para sua apresentação: http://channel9.msdn.com/Events/BUILD/BUILD2011/TOOL-835T Não deixe de ler também os comentários :)
fonte
for_each
nos permitem implementar o padrão de junção de garfo . Fora isso, ele suporta interface fluente .padrão de junção de garfo
Podemos adicionar implementação
gpu::for_each
para usar cuda / gpu para computação paralela heterogênea chamando a tarefa lambda em vários trabalhadores.E
gpu::for_each
pode esperar que os trabalhadores trabalhem em todas as tarefas lambda para concluir antes de executar as próximas instruções.interface fluente
Isso nos permite escrever código legível por humanos de maneira concisa.
fonte