Vantagens de std :: for_each over for loop

166

Existem vantagens do std::for_eachover forloop? Para mim, std::for_eachapenas parece dificultar a legibilidade do código. Por que alguns padrões de codificação recomendam seu uso?

desaparecido
fonte
10
std::for_eachquando usado com boost.lambdaou boost.bindpode melhorar a legibilidade
A pergunta e resposta aceita é de 2010. Para uma resposta mais uptodate (de 2018), consulte aqui: fluentcpp.com/2018/03/30/is-stdfor_each-obsolete
Erel Segal-Halevi

Respostas:

181

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

for(auto it = collection.begin(); it != collection.end() ; ++it)
{
   foo(*it);
}

Ou isto

for_each(collection.begin(), collection.end(), [](Element& e)
{
   foo(e);
});

quando a sintaxe do loop baseado em intervalofor estiver disponível:

for(Element& e : collection)
{
   foo(e);
}

Esse tipo de sintaxe está disponível em Java e C # há algum tempo e, na verdade, existem muito mais foreachloops que forloops clássicos em todos os códigos Java ou C # recentes que vi.

Thomas Petit
fonte
12
Na verdade, há muito tempo que um loop foreach com scoop está disponível, e eu ainda quero iterar com for_each e uma função lambda.
Viktor Sehr
19
A suposição de querer todo o intervalo de contêineres não faz parte da pergunta, portanto, essa é apenas uma resposta parcial.
Adrian McCarthy
6
Observe que fazer um loop sobre um elemento provavelmente não é a única coisa que você deseja fazer, portanto, pode ser uma boa ideia usar for_each apenas para que você aprenda sobre find / partition / copy_replace_if e os outros, o que realmente é muito para loops Faz.
Macke
10
O intervalo para é bom, exceto quando você realmente precisa do iterador (não há como acessá-lo).
Damon
5
Eu não usaria mesmo Element & eque auto & e(ou auto const &e) pareça melhor. Eu usaria Element 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 convertam Element.
Nawaz
53

Aqui estão alguns motivos:

  1. 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.)

  2. Ele permite que você escreva um algoritmo sobre for_each que funcione com qualquer iterador.

  3. Reduz a chance de erros estúpidos de digitação.

  4. 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_eachx 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):

for_each(monsters, boost::mem_fn(&Monster::think));

Ou com lambdas C ++ x11:

for_each(monsters, [](Monster& m) { m.think(); });

é IMO mais legível do que:

for(Monsters::iterator i = monsters.begin(); i != monsters.end(); ++i) {
    i->think();
} 

Também isso (ou com lambdas, veja outros):

for_each(bananas, boost::bind(&Monkey::eat, my_monkey, _1));

É mais conciso do que:

for(Bananas::iterator i = bananas.begin(); i != bananas.end(); ++i) {
    my_monkey->eat(*i);
} 

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?

Macke
fonte
+1, várias funções ou tipos aninhados: outer_class::inner_class::iteratorou são argumentos de modelo: typename std::vector<T>::iterator... o construtor for em si pode executar em si mesmo um construtor de muitas linhas
David Rodríguez - dribeas
4
(btw: a for_eachno segundo exemplo está incorreto (deve serfor_each( bananas.begin(), bananas.end(),...
David Rodríguez - dribeas
Eu escrevi wrappers que usam intervalos em vez de dois iteradores. Eles estarão disponíveis mais tarde (consulte range_ex), mas todos devem tê-los de qualquer maneira. (Adicionado atualização sobre isso.)
Macke
1
O suporte ao processamento paralelo é não. 1 razão aqui. Podemos adicionar implementação para usar cuda / gpu para computação paralela heterogênea.
Shuva 08/08/19
24

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 usada for_eachsem precisar atualizar o código de iteração. Você precisa considerar que existem outros contêineres no mundo além de std::vectormatrizes C simples para ver as vantagens de for_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:

std::vector<int> container;
...
std::for_each(container.begin(), container.end(), [](int& i){
    i+= 10;
});

Isso não parecerá estranho para você em três anos.

Terry Mahaffey
fonte
3
+1. Anúncio quando for_each pega um contêiner / intervalo em vez de dois iteradores, é ainda mais incrível.
Macke 12/01
2
@Marcus: Esse será o variaram-para construção ea sintaxe não vai ler 'for_each' em si mesmo: for ( int v : int_vector ) {(mesmo que pode ser simulado hoje com BOOST_FOREACH)
David Rodríguez - dribeas
@ David: Estou me referindo à adição geral das funções baseada em alcance (assim você pode usar faixas com todos estes for_each, copiar, remove_if, etc etc funções),
Macke
1
Por que não é possível escrever: std::for_each(container, [](int& i){ ... });. Quero dizer, por que alguém é forçado a escrever contêiner duas vezes?
Giorgio
1
@ Freitass: Escrever o contêiner uma vez, como no meu comentário anterior, poderia usar o iterador begin end sem chamá-los explicitamente. A maioria dos idiomas que fornecem funções de ordem superior nas coleções (Ruby, Scala, ...) escreve algo como container.each { ... }sem mencionar os iteradores de início e fim. Acho um pouco redundante precisar especificar o iterador final o tempo todo.
Giorgio #
17

Pessoalmente, sempre que eu precisar sair do meu caminho para usar std::for_each(escreva functors para fins especiais / boost::lambdas complicados ), acho que o BOOST_FOREACHC ++ 0x é baseado em intervalo para maior clareza:

BOOST_FOREACH(Monster* m, monsters) {
     if (m->has_plan()) 
         m->act();
}

vs

std::for_each(monsters.begin(), monsters.end(), 
  if_then(bind(&Monster::has_plan, _1), 
    bind(&Monster::act, _1)));
UncleBens
fonte
11

é 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_eachitslef é implementado como um loop

template<class InputIterator, class Function>
  Function for_each(InputIterator first, InputIterator last, Function f)
  {
    for ( ; first!=last; ++first ) f(*first);
    return f;
  }

então cabe a você escolher o que é certo para você.

Alon
fonte
11

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 :

for(std::vector<widget>::iterator i = v.begin(); i != v.end(); ++i)
{
   /////////////////////////////////////////////////////////////////////
   // Imagine a page of code here by programmers who don't refactor
   ///////////////////////////////////////////////////////////////////////
   if(widget->Cost < calculatedAmountSofar)
   {
        break;
   }
   ////////////////////////////////////////////////////////////////////////
   // And then some more code added by a stressed out juniour developer
   // *#&$*)#$&#(#)$#(*$&#(&*^$#(*$#)($*#(&$^#($*&#)$(#&*$&#*$#*)$(#*
   /////////////////////////////////////////////////////////////////////////
   for(std::vector<widgetPart>::iterator ip = widget.GetParts().begin(); ip != widget.GetParts().end(); ++ip)
   {
      if(ip->IsBroken())
      {
         return false;
      }
   }
}
Andrew Shepherd
fonte
1
Você faz um bom argumento, mas seu exemplo de motivação não é inteiramente justo. Quando você usa 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 o forloop 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 que std::for_each() reforça o alcance de toda a gama.
Wilhelmtell #
10

Você está mais correto: na maioria das vezes, std::for_eaché uma perda líquida. Eu iria tão longe para comparar for_eacha goto. gotofornece 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 um gotoisoladamente não diz praticamente nada sobre o que se pretende fazer nessa situação. Como resultado, quase ninguém em sã consciência usa, gotoexceto 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ão for_eachdiz praticamente nada sobre o que está sendo usado nessa situação. Infelizmente, a atitude das pessoas em relação ao local for_eaché onde elas gotoestavam (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 comum for_each. Eles acabam com algo como:

class XXX { 
// ...
public:
     std::ostream &print(std::ostream &os) { return os << "my data\n"; }
};

E seu posto está perguntando sobre qual combinação de bind1st, mem_fun, etc. Eles precisam fazer algo como:

std::vector<XXX> coll;

std::for_each(coll.begin(), coll.end(), XXX::print);

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:

std::ostream &operator<<(std::ostream *os, XXX const &x) { 
   return x.print(os);
}

e use std::copy:

std::copy(coll.begin(), coll.end(), std::ostream_iterator<XXX>(std::cout, "\n"));

Isso funciona - e praticamente não exige trabalho algum para descobrir que ele imprime o conteúdo de collpara std::cout.

Jerry Coffin
fonte
+1, embora haja um erro. No primeiro exemplo, deveria ser boost::mem_fn(&XXX::print)melhor queXXX::print
missingfaktor
Foi por isso que eu disse que esse exemplo não funcionaria, e eles estavam postando pedindo ajuda para fazê-lo funcionar (ah, e você também precisa vincular std::coutcomo argumento para que funcione).
Jerry Coffin
1
Embora geralmente seja uma boa resposta, a pergunta não é sobre o valor de for_each ou seu valor comparado a outros algoritmos std, mas sobre seu valor comparado a um loop for. Você pode pensar nisso nos casos em que nenhum outro algoritmo padrão se aplica. Você usaria um loop for_each ou for? Pense nisso e, o que quer que você tenha, essa deveria ter sido sua resposta.
Christian Rau
@ChristianRau: Sempre há uma linha tênue entre responder a pergunta exatamente como foi solicitado e tentar fornecer informações úteis. A resposta direta a exatamente as perguntas que ele faria seria "Provavelmente não. Quem sabe?", Mas seria inútil demais para valer a pena. Ao mesmo tempo, ir longe demais (por exemplo, recomendar Haskell em vez de qualquer um dos itens acima) provavelmente também não será muito útil.
Jerry Coffin
3
@ChristianRau: Como você acha que "... na maioria das vezes, std :: for_each é uma perda líquida" não aborda a questão de saber se std :: for_each oferece uma vantagem?
Jerry Coffin
8

A vantagem de escrever funcional para ser mais legível pode não aparecer quando for(...)e for_each(...).

Se você utiliza todos os algoritmos em functional.h, em vez de usar loops for, o código fica muito mais legível;

iterator longest_tree = std::max_element(forest.begin(), forest.end(), ...);
iterator first_leaf_tree = std::find_if(forest.begin(), forest.end(), ...);
std::transform(forest.begin(), forest.end(), firewood.begin(), ...);
std::for_each(forest.begin(), forest.end(), make_plywood);

é muito mais legível do que;

Forest::iterator longest_tree = it.begin();
for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{
   if (*it > *longest_tree) {
     longest_tree = it;
   }
}

Forest::iterator leaf_tree = it.begin();
for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{
   if (it->type() == LEAF_TREE) {
     leaf_tree  = it;
     break;
   }
}

for (Forest::const_iterator it = forest.begin(), jt = firewood.begin(); 
     it != forest.end(); 
     it++, jt++) {
          *jt = boost::transformtowood(*it);
    }

for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{
    std::makeplywood(*it);
}

E é isso que eu acho tão legal: generalize os for-loops para as funções de uma linha =)

Viktor Sehr
fonte
6

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

for_each(a.begin(), a.end(), a_item_handler);

é melhor que

for(auto& item: a) {
    a_item_handler(a);
}

Além disso, o forloop à distância itera apenas contêineres inteiros do início ao fim, enquanto for_eaché mais flexível.

Tigran Saluev
fonte
4

O for_eachloop 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.

struct myfunctor {
   void operator()( int arg1 ) { code }
};
void apply( std::vector<int> const & v ) {
   // code
   std::for_each( v.begin(), v.end(), myfunctor() );
   // more code
}

Observe que, se você deseja executar uma operação específica em cada objeto, pode usar std::mem_fn, ou boost::bind( std::bindno próximo padrão) ou boost::lambda(lambdas no próximo padrão) para simplificá-lo:

void function( int value );
void apply( std::vector<X> const & v ) {
   // code
   std::for_each( v.begin(), v.end(), boost::bind( function, _1 ) );
   // code
}

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_eachloop (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:

void apply( std::vector<int> const & v ) {
   // code
   struct myfunctor {
      void operator()( int ) { code }
   };
   std::for_each( v.begin(), v.end(), myfunctor() );
   // code
}

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:

void apply( std::vector<int> const & v ) {
   // code
   std::for_each( v.begin(), v.end(), 
      []( int ) { // code } );
   // code
}

Mesmo que, no caso de, for_eachexista uma construção específica que a torne mais natural:

void apply( std::vector<int> const & v ) {
   // code
   for ( int i : v ) {
      // code
   }
   // code
}

Costumo misturar a for_eachconstruçã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 a for_eachconstruçã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)

David Rodríguez - dribeas
fonte
3

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:

for (C::iterator iter = c.begin(); iter != c.end(); iter++) {
    do_something(*iter);
}

para:

C::iterator iter = c.begin();
C::iterator end = c.end();
while (iter != end) {
    do_something(*iter);
    ++iter;
}

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:

for_each(c.begin(), c.end(), do_something);

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.

Jason Govig
fonte
3

Eu não gostava std::for_eache 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_eachalgoritmo 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 com std::for_each, portanto, provavelmente mais fácil de compreender.

É std::for_eachmais provável que os funcores sejam mais genéricos e, portanto, reutilizáveis, por exemplo:

struct DeleteElement
{
    template <typename T>
    void operator()(const T *ptr)
    {
        delete ptr;
    }
};

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_eachque 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, transformetc.

Dmitry
fonte
2

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.

Kirill V. Lyadvinsky
fonte
4
não se você passar um iterador que avança em 3 com cada um ++. Incomum talvez, mas o loop for também está fazendo o mesmo.
Potatoswatter
Nesse caso, talvez seja melhor usar transformpara não confundir alguém.
Kirill V. Lyadvinsky
2

Se você usa frequentemente outros algoritmos do STL, há várias vantagens em for_each:

  1. Geralmente, é mais simples e menos propenso a erros do que um loop for, em parte porque você estará acostumado a funções com essa interface e em parte porque, na verdade, é um pouco mais conciso em muitos casos.
  2. Embora um loop for baseado em intervalo possa ser ainda mais simples, é menos flexível (como observado por Adrian McCarthy, ele itera sobre um contêiner inteiro).
  3. Ao contrário de um loop for tradicional, for_eachobriga a escrever um código que funcione para qualquer iterador de entrada. Restringir dessa maneira pode ser uma coisa boa porque:

    1. Você pode realmente precisar adaptar o código para trabalhar com um contêiner diferente posteriormente.
    2. No começo, isso pode lhe ensinar algo e / ou mudar seus hábitos para melhor.
    3. Mesmo que você sempre escreva para loops perfeitamente equivalentes, outras pessoas que modificam o mesmo código podem não fazer isso sem ser solicitado a usar for_each.
  4. 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 que for_eaché a melhor opção, mas um loop for não é a única alternativa.)

Sean Patrick Santos
fonte
2

Com o C ++ 11 e dois modelos simples, você pode escrever

        for ( auto x: range(v1+4,v1+6) ) {
                x*=2;
                cout<< x <<' ';
        }

como um substituto para for_eachou 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_eachsempre 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

template<typename iter>
struct range_ { 
                iter begin() {return __beg;}    iter end(){return __end;}
            range_(iter const&beg,iter const&end) : __beg(beg),__end(end) {}
            iter __beg, __end;
};

template<typename iter>
range_<iter> range(iter const &begin, iter const &end)
    { return range_<iter>(begin,end); }
jthill
fonte
1

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:

for_each(container, [](int& i) {
    i += 10;
});

Eu acho que isso é realmente mais legível do que um loop for. No entanto, isso requer as extensões do compilador C ++ 0x.

Dimitri C.
fonte
1

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

vector<thing>::iterator istart = container.begin();
vector<thing>::iterator iend = container.end();
for(vector<thing>::iterator i = istart; i != iend; ++i) {
  // Do stuff
}

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.

jcoder
fonte
0

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/

sdornan
fonte
2
As postagens somente de link não dão boas respostas e, de qualquer maneira, onde esse link mostra algo semelhante a um iterador de chamada? Tenho certeza de que esse conceito simplesmente não faz sentido. Talvez você estivesse apenas resumindo o for_eachque, nesse caso, não responde à pergunta sobre suas vantagens.
underscore_d
0

for_eachnos 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_eachpara usar cuda / gpu para computação paralela heterogênea chamando a tarefa lambda em vários trabalhadores.

gpu::for_each(users.begin(),users.end(),update_summary);
// all summary is complete now
// go access the user-summary here.

E gpu::for_eachpode 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.

accounts::erase(std::remove_if(accounts.begin(),accounts.end(),used_this_year));
std::for_each(accounts.begin(),accounts.end(),mark_dormant);
shuva
fonte