Vantaggi di std :: for_each over per loop


167

Ci sono dei vantaggi std::for_eachdell'over forloop? Per me, std::for_eachsembra solo ostacolare la leggibilità del codice. Perché allora alcuni standard di codifica ne raccomandano l'uso?


10
std::for_eachse usato con boost.lambdao boost.bindpuò spesso migliorare la leggibilità

La domanda e la risposta accettata provengono dal 2010. Per una risposta più aggiornata (dal 2018), vedere qui: fluentcpp.com/2018/03/30/is-stdfor_each-obsolete
Erel Segal-Halevi

Risposte:


181

La cosa bella di C ++ 11 (precedentemente chiamato C ++ 0x), è che questo noioso dibattito si risolverà.

Voglio dire, nessuno nella loro mente giusta, che vuole iterare su un'intera collezione, continuerà a usarlo

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

O questo

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

quando è disponibile la sintassi del loop basata su intervallofor :

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

Questo tipo di sintassi è disponibile da tempo in Java e C #, e in realtà ci sono molti più foreachloop rispetto ai forloop classici in ogni recente codice Java o C # che ho visto.


12
In realtà, un loop foreach con scoop è disponibile da molto tempo in boost e voglio ancora iterare con for_each e una funzione lambda.
Viktor Sehr,

19
L'ipotesi di volere l'intero intervallo di contenitori non fa parte della domanda, quindi questa è solo una risposta parziale.
Adrian McCarthy,

6
Nota che il loop su un elemento non è probabilmente l'unica cosa che vuoi fare, quindi può essere una buona idea usare for_each solo per imparare a trovare / partition / copy_replace_if e gli altri, che è molto per i loop fare.
Macke,

10
Range-for è bello, tranne quando in realtà hai bisogno dell'iteratore (quindi non c'è modo di arrivarci).
Damon,

5
Non userei nemmeno Element & ecome auto & e(o auto const &e) sembra migliore. Userei Element const e(senza riferimento) quando voglio una conversione implicita, ad esempio quando l'origine è una raccolta di diversi tipi e voglio che si convertano Element.
Nawaz,

54

Ecco alcuni motivi:

  1. Sembra ostacolare la leggibilità solo perché non ci sei abituato e / o non usi gli strumenti giusti per renderlo davvero facile. (vedi boost :: range e boost :: bind / boost :: lambda per gli helper. Molti di questi andranno in C ++ 0x e renderanno for_each e le funzioni correlate più utili.)

  2. Ti permette di scrivere un algoritmo sopra for_each che funziona con qualsiasi iteratore.

  3. Riduce la possibilità di stupidi bug di battitura.

  4. Si apre anche la vostra mente per il resto dei STL-algoritmi, come find_if, sort, replace, ecc e questi non sarà più così strano. Questa può essere una grande vittoria.

Aggiornamento 1:

for_eachAncora più importante, ti aiuta ad andare oltre rispetto ai cicli for come è tutto ciò che c'è, e guardare gli altri alog STL, come find / sort / partition / copy_replace_if, esecuzione parallela ... o qualsiasi altra cosa.

Molte elaborazioni possono essere scritte in modo molto conciso usando "il resto" dei fratelli di for_each, ma se tutto ciò che fai è scrivere un for-loop con varie logiche interne, allora non imparerai mai come usarli e finiscono per inventare la ruota ancora e ancora.

E (il prossimo range-style disponibile per_each):

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

O con lambda C ++ x11:

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

l'IMO è più leggibile di:

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

Anche questo (o con lambda, vedi gli altri):

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

È più conciso di:

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

Soprattutto se hai diverse funzioni da chiamare in ordine ... ma forse sono solo io. ;)

Aggiornamento 2 : ho scritto i miei wrapper one-liner di stl-algos che funzionano con intervalli anziché con coppie di iteratori. boost :: range_ex, una volta rilasciato, lo includerà e forse sarà presente anche in C ++ 0x?


+1, diverse funzioni o tipi nidificati: outer_class::inner_class::iteratoroppure sono argomenti modello: typename std::vector<T>::iterator... il costrutto stesso può incorrere in un costrutto a più righe in sé
David Rodríguez - dribeas

4
(a proposito: il for_eachsecondo esempio non è corretto (dovrebbe esserefor_each( bananas.begin(), bananas.end(),...
David Rodríguez - dribeas,

Ho scritto wrapper che utilizzano intervalli anziché due iteratori. Questi saranno disponibili in seguito (vedi range_ex) ma tutti dovrebbero comunque averli. (Aggiunto aggiornamento su questo.)
Macke,

1
Il supporto per l'elaborazione parallela è no. 1 motivo qui. Possiamo aggiungere l'implementazione per usare cuda / gpu per il calcolo eterogeneo-parallelo.
shuva,

25

for_eachè più generico. Puoi usarlo per iterare su qualsiasi tipo di contenitore (passando negli iteratori inizio / fine). È possibile scambiare potenzialmente contenitori sotto una funzione che utilizza for_eachsenza dover aggiornare il codice di iterazione. È necessario considerare che esistono altri contenitori al mondo oltre alle std::vectorsemplici vecchie matrici C per vedere i vantaggi di for_each.

Il principale svantaggio di for_eachè che richiede un functor, quindi la sintassi è goffa. Questo problema è stato risolto in C ++ 11 (precedentemente C ++ 0x) con l'introduzione di lambdas:

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

Questo non ti sembrerà strano per 3 anni.


3
+1. L'annuncio quando for_each accetta un contenitore / intervallo anziché due iteratori, è sempre più eccezionale.
Macke,

2
@Marcus: quello sarà il costrutto a distanza e la sintassi non leggerà 'for_each' in sé: for ( int v : int_vector ) {(anche se può essere simulato oggi con BOOST_FOREACH)
David Rodríguez - dribeas

@David: mi riferisco all'aggiunta generale di funzioni basate sull'intervallo (in modo da poter utilizzare gli intervalli con tutte queste funzioni for_each, copy, remove_if, ecc ecc.),
Macke

1
Perché non è possibile scrivere: std::for_each(container, [](int& i){ ... });. Voglio dire, perché si è costretti a scrivere due volte container?
Giorgio,

1
@freitass: scrivere il contenitore una volta come nel mio commento precedente potrebbe usare come predefinito l'iteratore di inizio iniziale senza chiamarli esplicitamente. La maggior parte dei linguaggi che forniscono funzioni di ordine superiore nelle raccolte (Ruby, Scala, ...) scrivono qualcosa di simile container.each { ... }senza menzionare gli iteratori di inizio e fine. Trovo un po 'ridondante che devo specificare sempre l'iteratore finale.
Giorgio,

18

Personalmente, ogni volta che dovrei andare fuori dal mio modo di usare std::for_each(scrivere funzioni speciali / complicati boost::lambda), trovo BOOST_FOREACHe la gamma basata su C ++ 0x per chiarezza:

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)));

12

è molto soggettivo, alcuni diranno che l'utilizzo for_each renderà il codice più leggibile, poiché consente di trattare diverse raccolte con le stesse convenzioni. for_eachitslef è implementato come un ciclo

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

quindi sta a te scegliere ciò che è giusto per te.


11

Come molte delle funzioni dell'algoritmo, una reazione iniziale è pensare che sia più illeggibile usare foreach che un loop. È stato argomento di molte guerre di fiamma.

Una volta che ti sei abituato al linguaggio potresti trovarlo utile. Un ovvio vantaggio è che forza il programmatore a separare il contenuto interno del loop dall'effettiva funzionalità di iterazione. (OK, penso che sia un vantaggio. Altri dicono che stai solo tagliando il codice senza un vero vantaggio).

Un altro vantaggio è che quando vedo foreach, so che o ogni articolo verrà elaborato o verrà generata un'eccezione.

Un ciclo for consente diverse opzioni per terminare il loop. Puoi lasciare che il ciclo esegua il suo corso completo oppure puoi usare la parola chiave break per saltare esplicitamente fuori dal ciclo o usare la parola chiave return per uscire dall'intera funzione a metà ciclo. Al contrario, foreach non consente queste opzioni e questo lo rende più leggibile. Puoi semplicemente dare un'occhiata al nome della funzione e conoscere la natura completa dell'iterazione.

Ecco un esempio di confusione per il ciclo:

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;
      }
   }
}

1
Hai un buon punto, ma il tuo esempio di motivazione non è del tutto giusto. Quando usi std::for_each()il vecchio standard (quello del tempo di questo post) devi usare un nominativo funzione, che incoraggia la leggibilità come dici tu e proibisce di uscire prematuramente dal ciclo. Ma poi il forciclo equivalente non ha nient'altro che una chiamata di funzione, e anche questo proibisce di scoppiare prematuramente. Ma a parte questo, penso che tu abbia fatto un ottimo punto nel dire che le std::for_each() forze attraversano l'intera gamma.
Wilhelmtell,

10

Per lo più hai ragione: il più delle volte, std::for_eachè una perdita netta. Mi piacerebbe andare così lontano da confrontare for_eacha goto. gotofornisce il controllo di flusso più versatile possibile: puoi utilizzarlo per implementare praticamente qualsiasi altra struttura di controllo che puoi immaginare. Quella versatilità, tuttavia, significa che vedere un gotoisolamento non ti dice praticamente nulla di ciò che è destinato a fare in questa situazione. Di conseguenza, quasi nessuno nella loro mente corretta usa gototranne come ultima risorsa.

Tra gli algoritmi standard, for_eachè più o meno allo stesso modo: può essere utilizzato per implementare praticamente qualsiasi cosa, il che significa che vedere non for_eachti dice praticamente nulla su ciò per cui viene utilizzato in questa situazione. Sfortunatamente, l'atteggiamento della gente nei confronti for_eachdi dove era il loro atteggiamento verso goto(diciamo) nel 1970 circa - alcune persone avevano capito che doveva essere usato solo come ultima risorsa, ma molti lo considerano ancora l'algoritmo primario, e raramente se mai ne usi un altro. La stragrande maggioranza delle volte, anche una rapida occhiata rivelerebbe che una delle alternative era drasticamente superiore.

Ad esempio, sono abbastanza sicuro di aver perso la traccia di quante volte ho visto persone scrivere codice per stampare il contenuto di una raccolta usando for_each. Sulla base dei post che ho visto, questo potrebbe essere l'uso più comune di for_each. Finiscono con qualcosa del tipo:

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

E il loro messaggio chiede su quale combinazione di bind1st, mem_funecc hanno bisogno di fare qualcosa di simile:

std::vector<XXX> coll;

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

lavorare e stampare gli elementi di coll. Se funzionasse davvero esattamente come l'ho scritto lì, sarebbe mediocre, ma non funziona - e quando lo farai funzionare, è difficile trovare quei pochi frammenti di codice relativi a ciò che è succedendo tra i pezzi che lo tengono insieme.

Fortunatamente, c'è un modo molto migliore. Aggiungi un sovraccarico dell'inseritore di flusso normale per XXX:

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

e usa std::copy:

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

Che fa il lavoro - e prende praticamente senza lavoro a tutti per capire che esso stampa il contenuto di colla std::cout.


+1, c'è un errore però. Nel primo esempio dovrebbe essere boost::mem_fn(&XXX::print)piuttosto cheXXX::print
missingfaktor il

Ecco perché ho detto che l'esempio non funzionerà, e stanno pubblicando messaggi per chiedere aiuto per farlo funzionare (oh, e devi anche legare std::coutcome argomento per farlo funzionare).
Jerry Coffin,

1
Sebbene sia generalmente una buona risposta, la domanda non riguarda il valore di for_each o il suo valore rispetto ad altri algoritmi std, ma il suo valore rispetto a un ciclo for. Potresti pensarci nei casi in cui non si applica nessun altro algoritmo std. Utilizzeresti quindi un ciclo for_each o un ciclo for? Pensaci e qualunque cosa ti venga in mente, quella avrebbe dovuto essere la tua risposta.
Christian Rau,

@ChristianRau: c'è sempre una linea sottile tra la risposta alla domanda esattamente come posta e il tentativo di fornire informazioni utili. La risposta diretta esattamente alle domande che avrebbe posto sarebbe "Probabilmente no. Chi lo sa?", Ma sarebbe troppo inutile per valere la pena. Allo stesso tempo, andare troppo lontano (es. Raccomandare Haskell invece di uno qualsiasi dei precedenti) non sarà probabilmente di grande utilità.
Jerry Coffin,

3
@ChristianRau: Come pensi che "... la maggior parte delle volte, std :: for_each è una perdita netta" non affronta la questione se std :: for_each offra un vantaggio?
Jerry Coffin,

8

Il vantaggio di scrivere funzionale per essere più leggibile, potrebbe non apparire quando for(...)e for_each(...).

Se si utilizzano tutti gli algoritmi in funzionali.h, invece di usare for-loops, il codice diventa molto più leggibile;

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);

è molto più leggibile di;

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);
}

Ed è quello che penso sia così bello, generalizzare i for-loop in una riga di funzioni =)


6

Facile: for_eachè utile quando hai già una funzione per gestire ogni elemento dell'array, quindi non devi scrivere un lambda. Certamente questo

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

è meglio di

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

Inoltre, il forciclo a distanza scorre solo su interi contenitori dall'inizio alla fine, mentre for_eachè più flessibile.


4

Il for_eachciclo ha lo scopo di nascondere gli iteratori (dettaglio di come viene implementato un ciclo) dal codice utente e definire una semantica chiara sull'operazione: ogni elemento verrà ripetuto esattamente una volta.

Il problema con la leggibilità nello standard attuale è che richiede un funzione come ultimo argomento anziché un blocco di codice, quindi in molti casi è necessario scrivere un tipo di funzione specifico per esso. Ciò si trasforma in codice meno leggibile poiché gli oggetti functor non possono essere definiti sul posto (le classi locali definite all'interno di una funzione non possono essere utilizzate come argomenti del modello) e l'implementazione del ciclo deve essere allontanata dal ciclo effettivo.

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
}

Si noti che se si desidera eseguire un'operazione specifica su ciascun oggetto, è possibile utilizzare std::mem_fn, oppure boost::bind( std::bindnello standard successivo) o boost::lambda(lambdas nello standard successivo) per semplificare:

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

Che non è meno leggibile e più compatto della versione arrotolata a mano se si dispone di una funzione / metodo da chiamare in atto. L'implementazione potrebbe fornire altre implementazioni del for_eachciclo (si pensi all'elaborazione parallela).

Lo standard imminente si occupa di alcune carenze in diversi modi, consentirà le classi definite localmente come argomenti per i modelli:

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

Migliorare la località del codice: quando navighi vedi cosa sta facendo proprio lì. È un dato di fatto, non è nemmeno necessario utilizzare la sintassi della classe per definire il functor, ma usare un lambda proprio lì:

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

Anche se per il caso for_eachci sarà un costrutto specifico che lo renderà più naturale:

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

Tendo a mescolare il for_eachcostrutto con anelli arrotolati a mano. Quando mi serve solo una chiamata a una funzione o un metodo esistente ( for_each( v.begin(), v.end(), boost::bind( &Type::update, _1 ) )) vado per il for_eachcostrutto che toglie al codice molte cose iteratore sulla piastra della caldaia. Quando ho bisogno di qualcosa di più complesso e non riesco a implementare un functor solo un paio di righe sopra l'uso effettivo, faccio scorrere il mio loop (mantiene l'operazione in atto). In sezioni di codice non critiche potrei andare con BOOST_FOREACH (un collaboratore mi ha coinvolto)


3

A parte la leggibilità e le prestazioni, un aspetto comunemente trascurato è la coerenza. Esistono molti modi per implementare un ciclo for (o while) sugli iteratori, da:

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

per:

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

con molti esempi in mezzo a vari livelli di efficienza e potenziale bug.

L'uso di for_each, tuttavia, rafforza la coerenza sottraendo il ciclo:

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

L'unica cosa di cui ti devi preoccupare ora è: implementi il ​​corpo del loop come funzione, funzione o lambda usando le funzionalità Boost o C ++ 0x? Personalmente, preferirei preoccuparmi di questo piuttosto che come implementare o leggere un ciclo casuale per / mentre.


3

Non mi piaceva std::for_eache pensavo che senza lambda fosse stato completamente sbagliato. Tuttavia ho cambiato idea qualche tempo fa, e ora lo adoro davvero. E penso che migliora anche la leggibilità e semplifica il test del codice in modo TDD.

L' std::for_eachalgoritmo può essere letto come fare qualcosa con tutti gli elementi nel raggio d'azione , il che può migliorare la leggibilità. Dire che l'azione che si desidera eseguire è lunga 20 righe e anche la funzione in cui viene eseguita l'azione è lunga circa 20 righe. Ciò renderebbe una funzione lunga 40 righe con un convenzionale per loop, e solo circa 20 con std::for_each, quindi probabilmente più facile da comprendere.

È std::for_eachpiù probabile che siano più generici e quindi riutilizzabili, ad esempio:

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

E nel codice avresti solo un one-liner come quello std::for_each(v.begin(), v.end(), DeleteElement())che è leggermente meglio IMO di un loop esplicito.

Tutti questi funzioni sono normalmente più facili da ottenere sotto test unitari di un esplicito ciclo nel mezzo di una lunga funzione, e questo da solo è già una grande vittoria per me.

std::for_each è anche generalmente più affidabile, poiché è meno probabile che tu commetta un errore con la portata.

E, infine, il compilatore potrebbe produrre un codice leggermente migliore std::for_eachrispetto a certi tipi di cicli fatti a mano, poiché (for_each) sembra sempre lo stesso per il compilatore e gli autori del compilatore possono mettere tutte le loro conoscenze, per renderlo buono come loro può.

Lo stesso vale per altri algoritmi std come find_if, transformecc.


2

forè per loop che può iterare ogni elemento o ogni terzo ecc. for_eachè per iterare solo ogni elemento. È chiaro dal suo nome. Quindi è più chiaro cosa intendi fare nel tuo codice.


4
non se gli passi un iteratore che avanza di 3 con ciascuno ++. Forse insolito, ma lo è anche un for-loop.
Potatoswatter il

In tal caso, forse sarà meglio usare transformper non confondere qualcuno.
Kirill V. Lyadvinsky il

2

Se usi frequentemente altri algoritmi dell'STL, ci sono diversi vantaggi per for_each:

  1. Sarà spesso più semplice e meno soggetto a errori rispetto a un ciclo for, in parte perché sarai abituato a funzioni con questa interfaccia e in parte perché in realtà è un po 'più conciso in molti casi.
  2. Sebbene un loop basato su range possa essere ancora più semplice, è meno flessibile (come notato da Adrian McCarthy, scorre su un intero contenitore).
  3. A differenza di un ciclo tradizionale, for_eachti costringe a scrivere codice che funzionerà per qualsiasi iteratore di input. Essere limitati in questo modo può effettivamente essere una buona cosa perché:

    1. Potrebbe essere necessario adattare il codice in modo che funzioni in seguito per un altro contenitore.
    2. All'inizio, potrebbe insegnarti qualcosa e / o cambiare le tue abitudini in meglio.
    3. Anche se scriveresti sempre per loop perfettamente equivalenti, altre persone che modificano lo stesso codice potrebbero non farlo senza che ti venga richiesto di utilizzarlo for_each.
  4. L'uso a for_eachvolte rende più ovvio che è possibile utilizzare una funzione STL più specifica per fare la stessa cosa. (Come nell'esempio di Jerry Coffin; non è necessariamente il caso che for_eachsia l'opzione migliore, ma un ciclo for non è l'unica alternativa.)


2

Con C ++ 11 e due modelli semplici, puoi scrivere

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

in sostituzione for_eacho in loop. Perché scegliere si riduce a brevità e sicurezza, non c'è possibilità di errore in un'espressione che non c'è.

Per me, for_eachè sempre stato meglio per gli stessi motivi quando il corpo del loop è già un functor e ne trarrò vantaggio.

Usi ancora l'espressione a tre for, ma ora quando ne vedi una sai che c'è qualcosa da capire lì, non è una piastra di comando. Io odio il boilerplate. Mi risento per la sua esistenza. Non è un vero codice, non c'è nulla da imparare leggendolo, è solo un'altra cosa che deve essere verificata. Lo sforzo mentale può essere misurato da quanto sia facile essere arrugginiti nel controllarlo.

I modelli sono

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); }

1

Principalmente dovrai ripetere l'intera collezione . Pertanto ti consiglio di scrivere la tua variante for_each (), prendendo solo 2 parametri. Questo ti permetterà di riscrivere l'esempio di Terry Mahaffey come:

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

Penso che questo sia davvero più leggibile di un ciclo for. Tuttavia, ciò richiede le estensioni del compilatore C ++ 0x.


1

Trovo che for_each sia dannoso per la leggibilità. Il concetto è valido ma c ++ rende molto difficile scrivere leggibile, almeno per me. Le espressioni lamda c ++ 0x aiuteranno. Mi piace molto l'idea dei lama. Tuttavia a prima vista penso che la sintassi sia molto brutta e non sono sicuro al 100% che mi abituerò mai. Forse tra 5 anni mi ci sono abituato e non ci ripenso, ma forse no. Il tempo lo dirà :)

Preferisco usare

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

Trovo esplicito che il loop sia più chiaro da leggere e che esplicitamente usando variabili nominate per gli iteratori di inizio e fine riduce il disordine nel ciclo for.

Naturalmente i casi variano, questo è quello che di solito trovo meglio.


0

Puoi far sì che l'iteratore sia una chiamata a una funzione che viene eseguita su ogni iterazione attraverso il ciclo.

Vedi qui: http://www.cplusplus.com/reference/algorithm/for_each/


2
I post solo link non danno buone risposte, e comunque, dove in quel link mostra qualcosa che assomiglia a un iteratore richiamabile? Sono abbastanza sicuro che questo concetto non abbia senso. Forse stavi solo riassumendo cosa for_eachfa, nel qual caso non risponde alla domanda sui suoi vantaggi.
underscore_d


0

for_eachci permettono di implementare il modello Fork-Join . Oltre a ciò, supporta l' interfaccia fluente .

modello fork-join

È possibile aggiungere l'implementazione gpu::for_eachper utilizzare cuda / gpu per l'elaborazione eterogenea parallela chiamando l'attività lambda in più lavoratori.

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

E gpu::for_eachpuò aspettare che i lavoratori lavorino su tutte le attività lambda per finire prima di eseguire le dichiarazioni successive.

fluente interfaccia

Ci consente di scrivere codice leggibile dall'uomo in modo conciso.

accounts::erase(std::remove_if(accounts.begin(),accounts.end(),used_this_year));
std::for_each(accounts.begin(),accounts.end(),mark_dormant);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.