Ventajas de std :: for_each over for loop

167

¿Hay alguna ventaja de std::for_eachover forloop? Para mí, std::for_eachsolo parece obstaculizar la legibilidad del código. ¿Por qué algunos estándares de codificación recomiendan su uso?

missingfaktor
fuente
10
std::for_eachcuando se usa con boost.lambdao a boost.bindmenudo puede mejorar la legibilidad
La pregunta y la respuesta aceptada son de 2010. Para obtener una respuesta más actualizada (de 2018), consulte aquí: fluentcpp.com/2018/03/30/is-stdfor_each-obsolete
Erel Segal-Halevi

Respuestas:

181

Lo bueno de C ++ 11 (anteriormente llamado C ++ 0x) es que este debate agotador se resolverá.

Quiero decir, nadie en su sano juicio, que quiera iterar sobre una colección completa, seguirá usando esto

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

O esto

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

cuando la sintaxis de bucle basada en rangofor está disponible:

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

Este tipo de sintaxis ha estado disponible en Java y C # desde hace algún tiempo, y en realidad hay muchos más foreachbucles que forbucles clásicos en cada código Java o C # reciente que vi.

Thomas Petit
fuente
12
En realidad, un bucle foreach con scoop ha estado disponible durante mucho tiempo en boost, y todavía quiero iterar con for_each y una función lambda.
Viktor Sehr
19
El supuesto de querer todo el rango de contenedores no es parte de la pregunta, por lo que esta es solo una respuesta parcial.
Adrian McCarthy
66
Tenga en cuenta que recorrer un elemento probablemente no sea lo único que quiera hacer, por lo que puede ser una buena idea usar for_each solo para que aprenda sobre find / division / copy_replace_if y los demás, que es lo que realmente hace muchos bucles. hacer.
Macke
10
Range-for es bueno, excepto cuando realmente necesita el iterador (entonces no hay forma de llegar a él).
Damon
55
No lo usaría incluso Element & ecuando auto & e(o auto const &e) se vea mejor. Usaría Element const e(sin referencia) cuando quiero una conversión implícita, digamos cuando la fuente es una colección de diferentes tipos, y quiero que se conviertan Element.
Nawaz
54

Aquí hay algunas razones:

  1. Parece dificultar la legibilidad solo porque no estás acostumbrado y / o no estás usando las herramientas adecuadas para hacerlo realmente fácil. (vea boost :: range y boost :: bind / boost :: lambda para ayudantes. Muchos de estos irán a C ++ 0x y harán que for_each y las funciones relacionadas sean más útiles).

  2. Le permite escribir un algoritmo encima de for_each que funciona con cualquier iterador.

  3. Reduce la posibilidad de estúpidos errores de tipeo.

  4. También abre su mente para el resto de los algoritmos de STL, como find_if, sort, replace, etc, y estos no se verá tan extraño más. Esto puede ser una gran victoria.

Actualización 1:

Lo que es más importante, te ayuda a ir más allá for_eachde los bucles for-for como si fuera todo lo que hay, y mira los otros STL-alogs, como find / sort / division / copy_replace_if, ejecución paralela ... o lo que sea.

Se puede escribir una gran cantidad de procesamiento de manera muy concisa usando "el resto" de los hermanos de for_each, pero si todo lo que hace es escribir un bucle for con varias lógicas internas, nunca aprenderá cómo usarlos, y terminar inventando la rueda una y otra vez.

Y (el estilo de rango disponible próximamente para cada uno):

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

O con C ++ x11 lambdas:

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

es IMO más legible que:

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

También esto (o con lambdas, ver otros):

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

Es más conciso que:

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

Especialmente si tienes varias funciones para llamar en orden ... pero tal vez solo soy yo. ;)

Actualización 2 : he escrito mis propios envoltorios de una línea de stl-algos que funcionan con rangos en lugar de un par de iteradores. boost :: range_ex, una vez lanzado, incluirá eso y tal vez también estará allí en C ++ 0x?

Macke
fuente
+1, varias funciones o tipos anidados: outer_class::inner_class::iteratoro son argumentos de plantilla: typename std::vector<T>::iterator... el for for sí mismo puede encontrarse con una construcción de muchas líneas en sí mismo
David Rodríguez - dribeas
44
(por cierto: el for_eachen el segundo ejemplo es incorrecto (debería serfor_each( bananas.begin(), bananas.end(),...
David Rodríguez - dribeas
He escrito contenedores que usan rangos en lugar de dos iteradores. Estos estarán disponibles más tarde (ver range_ex) pero todos deberían tenerlos de todos modos. (Se agregó una actualización sobre eso.)
Macke
1
El soporte de procesamiento paralelo es no. 1 razón aquí. Podemos agregar implementación para usar cuda / gpu para computación heterogénea-paralela.
shuva
25

for_eachEs más genérico. Puede usarlo para iterar sobre cualquier tipo de contenedor (pasando los iteradores de inicio / fin). Potencialmente, puede intercambiar contenedores debajo de una función que utiliza for_eachsin tener que actualizar el código de iteración. Debe tener en cuenta que existen otros contenedores en el mundo que no sean std::vectory matrices de C antiguas para ver las ventajas de for_each.

El principal inconveniente de for_eaches que se necesita un functor, por lo que la sintaxis es torpe. Esto se soluciona en C ++ 11 (anteriormente C ++ 0x) con la introducción de lambdas:

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

Esto no te parecerá extraño en 3 años.

Terry Mahaffey
fuente
3
+1. Anuncio cuando for_each toma un contenedor / rango en lugar de dos iteradores, es cada vez más increíble.
Macke
2
@Marcus: esa será la construcción a distancia y la sintaxis no leerá 'for_each' en sí misma: for ( int v : int_vector ) {(incluso si se puede simular hoy con BOOST_FOREACH)
David Rodríguez - dribeas
@ David: Me refiero a la adición general de las funciones basados en la esfera (lo que puede utilizar rangos con todos estos for_each, copiar funciones, remove_if, etc, etc),
Macke
1
Por qué no es posible escribir: std::for_each(container, [](int& i){ ... });. Quiero decir, ¿por qué uno se ve obligado a escribir el contenedor dos veces?
Giorgio
1
@freitass: escribir el contenedor una vez como en mi comentario anterior podría usar de manera predeterminada el iterador de inicio y fin sin llamarlos explícitamente. La mayoría de los lenguajes que proporcionan funciones de orden superior en colecciones (Ruby, Scala, ...) escriben algo así container.each { ... }sin mencionar los iteradores de inicio y fin. Me parece un poco redundante que tenga que especificar el iterador final todo el tiempo.
Giorgio
18

Personalmente, cada vez que necesito salir de mi camino para usar std::for_each(escribir functores de propósito especial / boost::lambdas complicados ), encuentro BOOST_FOREACHy C ++ 0x basado en el rango para más claro:

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)));
TíoBens
fuente
12

Es muy subjetivo, algunos dirán que el uso for_each hará que el código sea más legible, ya que permite tratar diferentes colecciones con las mismas convenciones. for_eachitslef se implementa como un bucle

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

así que depende de usted elegir lo que es adecuado para usted.

solo
fuente
11

Como muchas de las funciones del algoritmo, una reacción inicial es pensar que es más ilegible usar foreach que un bucle. Ha sido un tema de muchas guerras de llamas.

Una vez que te acostumbres al idioma, te puede resultar útil. Una ventaja obvia es que obliga al codificador a separar el contenido interno del bucle de la funcionalidad de iteración real. (OK, creo que es una ventaja. Otros dicen que solo estás cortando el código sin ningún beneficio real).

Otra ventaja es que cuando veo foreach, que cada elemento será procesado o se lanzará una excepción.

Un bucle for permite varias opciones para terminar el bucle. Puede dejar que el ciclo siga su curso completo, o puede usar la palabra clave break para saltar explícitamente del ciclo, o usar la palabra clave return para salir de toda la función mid-loop. Por el contrario, foreach no permite estas opciones, y esto lo hace más legible. Puede echar un vistazo al nombre de la función y conocer la naturaleza completa de la iteración.

Aquí hay un ejemplo de un ciclo 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
fuente
1
Haces un buen punto, pero tu ejemplo de motivación no es del todo justo. Cuando se usa std::for_each()en el estándar anterior (el de la época de esta publicación), debe usar un functor con nombre, que fomenta la legibilidad como dice y prohíbe salir del bucle prematuramente. Pero entonces el forbucle equivalente no tiene más que una llamada a función, y eso también prohíbe la ruptura prematura. Pero aparte de eso, creo que hiciste un excelente comentario al decir que se std::for_each() impone pasar por todo el rango.
wilhelmtell
10

En su mayoría tiene razón: la mayoría de las veces, std::for_eaches una pérdida neta. Me gustaría ir tan lejos como para comparar for_eacha goto. gotoproporciona el control de flujo más versátil posible: puede usarlo para implementar prácticamente cualquier otra estructura de control que pueda imaginar. Sin embargo, esa misma versatilidad significa que ver un gotoaislamiento no le dice prácticamente nada sobre lo que se pretende hacer en esta situación. Como resultado, casi nadie en su sano juicio lo usa gotoexcepto como último recurso.

Entre los algoritmos estándar, for_eaches muy similar: se puede usar para implementar prácticamente cualquier cosa, lo que significa que ver no for_eachle dice prácticamente nada sobre para qué se usa en esta situación. Desafortunadamente, la actitud de la gente hacia for_eaches acerca de dónde estaba su actitud hacia goto(digamos) 1970 más o menos: algunas personas se dieron cuenta del hecho de que debería usarse solo como último recurso, pero muchos todavía lo consideran el algoritmo principal, y rara vez, si alguna vez, usa alguna otra. La gran mayoría de las veces, incluso una rápida mirada revelaría que una de las alternativas era drásticamente superior.

Solo por ejemplo, estoy bastante seguro de que he perdido la cuenta de cuántas veces he visto a personas escribir código para imprimir el contenido de una colección usando for_each. Según las publicaciones que he visto, este podría ser el uso más común de for_each. Terminan con algo como:

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

Y su puesto está preguntando por qué combinación de bind1st, mem_fun, etc. Tienen que hacer algo como:

std::vector<XXX> coll;

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

trabajar e imprimir los elementos de coll. Si realmente funcionó exactamente como lo escribí allí, sería mediocre, pero no lo hace, y para cuando lo hayas hecho funcionar, es difícil encontrar esos pocos bits de código relacionados con lo que hay pasando entre las piezas que lo mantienen unido.

Afortunadamente, hay una manera mucho mejor. Agregue una sobrecarga normal del insertador de flujo para XXX:

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

y uso std::copy:

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

Que hace el trabajo - y toma prácticamente ningún trabajo en absoluto a darse cuenta de que se imprime el contenido de colla std::cout.

Jerry Coffin
fuente
+1, aunque hay un error. En el primer ejemplo, debería ser en boost::mem_fn(&XXX::print)lugar deXXX::print
missingfaktor
Es por eso que dije que el ejemplo no funcionará, y están publicando pidiendo ayuda para que funcione (oh, y también debes vincularlo std::coutcomo argumento para que funcione).
Jerry Coffin
1
Aunque generalmente es una buena respuesta, la pregunta no es sobre el valor de for_each o su valor en comparación con otros algoritmos estándar, sino sobre su valor en comparación con un bucle for. Puede pensar en casos en los que no se aplica ningún otro algoritmo estándar. ¿Usarías un bucle for_each o for? Piénsalo y lo que sea que se te ocurra, esa debería haber sido tu respuesta.
Christian Rau
@ChristianRau: Siempre hay una línea muy fina entre responder la pregunta exactamente como se la hizo e intentar proporcionar información útil. La respuesta directa a exactamente las preguntas que hizo sería "Probablemente no. ¿Quién sabe?", Pero sería demasiado inútil para que valga la pena. Al mismo tiempo, ir demasiado lejos (por ejemplo, recomendar Haskell en lugar de cualquiera de los anteriores) tampoco es muy útil.
Jerry Coffin
3
@ChristianRau: ¿Cómo crees que "... la mayoría de las veces, std :: for_each es una pérdida neta" no aborda la cuestión de si std :: for_each ofrece una ventaja?
Jerry Coffin
8

La ventaja de la escritura funcional para ser más legible, podría no aparecer cuando for(...)y for_each(...).

Si utiliza todos los algoritmos en functional.h, en lugar de usar for-loops, el código se vuelve mucho más legible;

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

es mucho más legible 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);
}

Y eso es lo que creo que es tan bueno, generalice los bucles for a funciones de una línea =)

Viktor Sehr
fuente
6

Fácil: for_eaches útil cuando ya tiene una función para manejar cada elemento de la matriz, por lo que no tiene que escribir una lambda. Ciertamente, esto

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

es mejor que

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

Además, el forbucle a distancia solo itera sobre contenedores completos de principio a fin, mientras que for_eaches más flexible.

Tigran Saluev
fuente
4

El for_eachbucle está destinado a ocultar los iteradores (detalle de cómo se implementa un bucle) del código de usuario y definir una semántica clara en la operación: cada elemento se repetirá exactamente una vez.

El problema con la legibilidad en el estándar actual es que requiere un functor como último argumento en lugar de un bloque de código, por lo que en muchos casos debe escribir un tipo de functor específico para él. Eso se convierte en un código menos legible ya que los objetos de functor no pueden definirse en el lugar (las clases locales definidas dentro de una función no pueden usarse como argumentos de plantilla) y la implementación del bucle debe alejarse del bucle 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
}

Tenga en cuenta que si desea realizar una operación específica en cada objeto, puede usar std::mem_fno boost::bind( std::binden el siguiente estándar) o boost::lambda(lambdas en el siguiente estándar) para simplificarlo:

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

Que no es menos legible y más compacto que la versión enrollada a mano si tiene una función / método para llamar. La implementación podría proporcionar otras implementaciones del for_eachbucle (piense en el procesamiento paralelo).

El próximo estándar se ocupa de algunas de las deficiencias de diferentes maneras, permitirá clases definidas localmente como argumentos para las plantillas:

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

Mejorando la localidad del código: cuando navegas ves lo que está haciendo allí. De hecho, ni siquiera necesita usar la sintaxis de clase para definir el functor, pero use una lambda allí:

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

Incluso si para el caso de for_eachhabrá una construcción específica que lo hará más natural:

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

Tiendo a mezclar la for_eachconstrucción con bucles enrollados a mano. Cuando solo necesito una llamada a una función o método existente ( for_each( v.begin(), v.end(), boost::bind( &Type::update, _1 ) )), voy por la for_eachconstrucción que le quita al código muchas cosas de iterador de placa de caldera. Cuando necesito algo más complejo y no puedo implementar un functor solo un par de líneas por encima del uso real, enrollo mi propio bucle (mantiene la operación en su lugar). En secciones de código no críticas, podría ir con BOOST_FOREACH (un compañero de trabajo me metió en eso)

David Rodríguez - dribeas
fuente
3

Además de la legibilidad y el rendimiento, un aspecto comúnmente ignorado es la consistencia. Hay muchas formas de implementar un ciclo for (o while) sobre iteradores, desde:

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

a:

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

con muchos ejemplos intermedios en diferentes niveles de eficiencia y potencial de error.

Sin embargo, el uso de for_each impone la coherencia al abstraer el ciclo:

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

Lo único de lo que debe preocuparse ahora es: ¿implementa el cuerpo del bucle como función, functor o lambda utilizando las funciones Boost o C ++ 0x? Personalmente, prefiero preocuparme por eso que por cómo implementar o leer un bucle aleatorio para / mientras.

Jason Govig
fuente
3

No me gustaba std::for_eachy pensaba que sin lambda, estaba completamente equivocado. Sin embargo, cambié de opinión hace algún tiempo, y ahora realmente me encanta. Y creo que incluso mejora la legibilidad y hace que sea más fácil probar su código de forma TDD.

El std::for_eachalgoritmo se puede leer como hacer algo con todos los elementos dentro del rango , lo que puede mejorar la legibilidad. Digamos que la acción que desea realizar es de 20 líneas de largo, y la función donde se realiza la acción también tiene aproximadamente 20 líneas de largo. Eso haría una función de 40 líneas de largo con un bucle convencional, y solo alrededor de 20 con std::for_each, por lo tanto, probablemente sea más fácil de comprender.

Los functores para std::for_eachson más propensos a ser más genéricos y, por lo tanto, reutilizables, por ejemplo:

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

Y en el código solo tendrías una línea como std::for_each(v.begin(), v.end(), DeleteElement())IMO ligeramente mejor que un bucle explícito.

Todos esos functores son normalmente más fáciles de obtener bajo pruebas unitarias que un bucle for explícito en medio de una función larga, y eso solo ya es una gran victoria para mí.

std::for_each También es generalmente más confiable, ya que es menos probable que cometas un error con el rango.

Y, por último, el compilador puede producir un código ligeramente mejor std::for_eachque para ciertos tipos de bucles for hechos a mano, ya que (para cada uno) siempre se ve igual para el compilador, y los escritores del compilador pueden poner todo su conocimiento, para que sea tan bueno como ellos lata.

Lo mismo se aplica a otros algoritmos estándar como find_if, transformetc.

Dmitry
fuente
2

fores para un ciclo que puede iterar cada elemento o cada tercio, etc. for_eaches para iterar solo cada elemento. Está claro por su nombre. Por lo tanto, es más claro lo que tiene la intención de hacer en su código.

Kirill V. Lyadvinsky
fuente
44
no si le pasa un iterador que avanza en 3 con cada uno ++. Inusual tal vez, pero también lo es un bucle for.
Potatoswatter
En ese caso, tal vez sea mejor usar transformpara no confundir a alguien.
Kirill V. Lyadvinsky
2

Si utiliza con frecuencia otros algoritmos de STL, existen varias ventajas para for_each:

  1. A menudo será más simple y menos propenso a errores que un bucle for, en parte porque estará acostumbrado a las funciones con esta interfaz y en parte porque en realidad es un poco más conciso en muchos casos.
  2. Aunque un bucle for basado en rango puede ser aún más simple, es menos flexible (como señaló Adrian McCarthy, itera sobre un contenedor completo).
  3. A diferencia de un bucle for tradicional, for_eachte obliga a escribir código que funcione para cualquier iterador de entrada. Estar restringido de esta manera puede ser algo bueno porque:

    1. Es posible que más tarde necesite adaptar el código para que funcione para un contenedor diferente.
    2. Al principio, podría enseñarle algo y / o cambiar sus hábitos para mejor.
    3. Incluso si siempre escribiera para bucles que son perfectamente equivalentes, otras personas que modifiquen el mismo código podrían no hacerlo sin que se les solicite su uso for_each.
  4. El uso a for_eachveces hace que sea más obvio que puede usar una función STL más específica para hacer lo mismo. (Como en el ejemplo de Jerry Coffin; no es necesariamente el caso que for_eachsea ​​la mejor opción, pero un bucle for no es la única alternativa).

Sean Patrick Santos
fuente
2

Con C ++ 11 y dos plantillas simples, puedes escribir

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

como reemplazo for_eacho bucle. Por qué elegirlo se reduce a brevedad y seguridad, no hay posibilidad de error en una expresión que no está allí.

Para mí, for_eachsiempre fue mejor por el mismo motivo cuando el cuerpo del bucle ya es un ficticio, y aprovecharé cualquier ventaja que pueda obtener.

Todavía usa la expresión de tres for, pero ahora cuando ve una, sabe que hay algo que entender allí, no es repetitivo. Yo odio repetitivo. Me molesta su existencia. No es un código real, no hay nada que aprender al leerlo, es solo una cosa más que debe verificarse. El esfuerzo mental se puede medir por lo fácil que es oxidarse al comprobarlo.

Las plantillas son

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
fuente
1

En su mayoría, tendrá que recorrer toda la colección . Por lo tanto, le sugiero que escriba su propia variante for_each (), tomando solo 2 parámetros. Esto le permitirá reescribir el ejemplo de Terry Mahaffey como:

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

Creo que esto es más legible que un bucle for. Sin embargo, esto requiere las extensiones del compilador C ++ 0x.

Dimitri C.
fuente
1

Encuentro que for_each es malo para la legibilidad. El concepto es bueno, pero c ++ hace que sea muy difícil escribir legible, al menos para mí. Las expresiones c ++ 0x lamda ayudarán. Me gusta mucho la idea de lamdas. Sin embargo, a primera vista, creo que la sintaxis es muy fea y no estoy 100% seguro de que alguna vez me acostumbre. Tal vez en 5 años me haya acostumbrado y no lo piense más, pero tal vez no. El tiempo dirá :)

Prefiero usar

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

Encuentro un explícito para el bucle más claro para leer y la explicidad utilizando variables con nombre para los iteradores de inicio y final reduce el desorden en el bucle for.

Por supuesto, los casos varían, esto es justo lo que generalmente encuentro mejor.

jcoder
fuente
0

Puede hacer que el iterador sea una llamada a una función que se realiza en cada iteración a través del bucle.

Ver aquí: http://www.cplusplus.com/reference/algorithm/for_each/

sdornan
fuente
2
Las publicaciones solo de enlace no dan buenas respuestas, y de todos modos, ¿en qué lugar de ese enlace muestra algo parecido a un iterador invocable? Estoy bastante seguro de que ese concepto simplemente no tiene sentido. Tal vez solo estaba resumiendo lo que for_eachhace, en cuyo caso, no responde la pregunta sobre sus ventajas.
underscore_d
0

for_eachpermítanos implementar el patrón Fork-Join . Aparte de eso, admite una interfaz fluida .

patrón de horquilla

Podemos agregar implementación gpu::for_eachpara usar cuda / gpu para computación heterogénea-paralela llamando a la tarea lambda en múltiples trabajadores.

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

Y gpu::for_eachpuede esperar a que los trabajadores trabajen en todas las tareas lambda para finalizar antes de ejecutar las siguientes declaraciones.

interfaz fluida

Nos permite escribir código legible por humanos de manera concisa.

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