¿Hay alguna ventaja de std::for_each
over for
loop? Para mí, std::for_each
solo parece obstaculizar la legibilidad del código. ¿Por qué algunos estándares de codificación recomiendan su uso?
c++
stl
foreach
coding-style
missingfaktor
fuente
fuente
std::for_each
cuando se usa conboost.lambda
o aboost.bind
menudo puede mejorar la legibilidadRespuestas:
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
O esto
cuando la sintaxis de bucle basada en rango
for
está disponible:Este tipo de sintaxis ha estado disponible en Java y C # desde hace algún tiempo, y en realidad hay muchos más
foreach
bucles quefor
bucles clásicos en cada código Java o C # reciente que vi.fuente
Element & e
cuandoauto & e
(oauto const &e
) se vea mejor. UsaríaElement 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 conviertanElement
.Aquí hay algunas razones:
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).
Le permite escribir un algoritmo encima de for_each que funciona con cualquier iterador.
Reduce la posibilidad de estúpidos errores de tipeo.
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_each
de 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):
O con C ++ x11 lambdas:
es IMO más legible que:
También esto (o con lambdas, ver otros):
Es más conciso que:
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?
fuente
outer_class::inner_class::iterator
o 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í mismofor_each
en el segundo ejemplo es incorrecto (debería serfor_each( bananas.begin(), bananas.end(),...
for_each
Es 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 utilizafor_each
sin tener que actualizar el código de iteración. Debe tener en cuenta que existen otros contenedores en el mundo que no seanstd::vector
y matrices de C antiguas para ver las ventajas defor_each
.El principal inconveniente de
for_each
es 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:Esto no te parecerá extraño en 3 años.
fuente
for ( int v : int_vector ) {
(incluso si se puede simular hoy con BOOST_FOREACH)std::for_each(container, [](int& i){ ... });
. Quiero decir, ¿por qué uno se ve obligado a escribir el contenedor dos veces?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.Personalmente, cada vez que necesito salir de mi camino para usar
std::for_each
(escribir functores de propósito especial /boost::lambda
s complicados ), encuentroBOOST_FOREACH
y C ++ 0x basado en el rango para más claro:vs
fuente
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_each
itslef se implementa como un bucleasí que depende de usted elegir lo que es adecuado para usted.
fuente
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, sé 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 :
fuente
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 elfor
bucle 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 sestd::for_each()
impone pasar por todo el rango.En su mayoría tiene razón: la mayoría de las veces,
std::for_each
es una pérdida neta. Me gustaría ir tan lejos como para compararfor_each
agoto
.goto
proporciona 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 ungoto
aislamiento 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 usagoto
excepto como último recurso.Entre los algoritmos estándar,
for_each
es muy similar: se puede usar para implementar prácticamente cualquier cosa, lo que significa que ver nofor_each
le dice prácticamente nada sobre para qué se usa en esta situación. Desafortunadamente, la actitud de la gente haciafor_each
es acerca de dónde estaba su actitud haciagoto
(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 defor_each
. Terminan con algo como:Y su puesto está preguntando por qué combinación de
bind1st
,mem_fun
, etc. Tienen que hacer algo como: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:
y uso
std::copy
:Que hace el trabajo - y toma prácticamente ningún trabajo en absoluto a darse cuenta de que se imprime el contenido de
coll
astd::cout
.fuente
boost::mem_fn(&XXX::print)
lugar deXXX::print
std::cout
como argumento para que funcione).La ventaja de la escritura funcional para ser más legible, podría no aparecer cuando
for(...)
yfor_each(...
).Si utiliza todos los algoritmos en functional.h, en lugar de usar for-loops, el código se vuelve mucho más legible;
es mucho más legible que;
Y eso es lo que creo que es tan bueno, generalice los bucles for a funciones de una línea =)
fuente
Fácil:
for_each
es útil cuando ya tiene una función para manejar cada elemento de la matriz, por lo que no tiene que escribir una lambda. Ciertamente, estoes mejor que
Además, el
for
bucle a distancia solo itera sobre contenedores completos de principio a fin, mientras quefor_each
es más flexible.fuente
El
for_each
bucle 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.
Tenga en cuenta que si desea realizar una operación específica en cada objeto, puede usar
std::mem_fn
oboost::bind
(std::bind
en el siguiente estándar) oboost::lambda
(lambdas en el siguiente estándar) para simplificarlo: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_each
bucle (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:
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í:
Incluso si para el caso de
for_each
habrá una construcción específica que lo hará más natural:Tiendo a mezclar la
for_each
construcció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 lafor_each
construcció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)fuente
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:
a:
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:
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.
fuente
No me gustaba
std::for_each
y 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_each
algoritmo 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 constd::for_each
, por lo tanto, probablemente sea más fácil de comprender.Los functores para
std::for_each
son más propensos a ser más genéricos y, por lo tanto, reutilizables, por ejemplo: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_each
que 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
,transform
etc.fuente
for
es para un ciclo que puede iterar cada elemento o cada tercio, etc.for_each
es 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.fuente
++
. Inusual tal vez, pero también lo es un bucle for.transform
para no confundir a alguien.Si utiliza con frecuencia otros algoritmos de STL, existen varias ventajas para
for_each
:A diferencia de un bucle for tradicional,
for_each
te obliga a escribir código que funcione para cualquier iterador de entrada. Estar restringido de esta manera puede ser algo bueno porque:for_each
.El uso a
for_each
veces 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 quefor_each
sea la mejor opción, pero un bucle for no es la única alternativa).fuente
Con C ++ 11 y dos plantillas simples, puedes escribir
como reemplazo
for_each
o 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_each
siempre 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
fuente
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:
Creo que esto es más legible que un bucle for. Sin embargo, esto requiere las extensiones del compilador C ++ 0x.
fuente
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
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.
fuente
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/
fuente
for_each
hace, en cuyo caso, no responde la pregunta sobre sus ventajas.Porque el bucle puede romperse; No quiero ser un loro para Herb Sutter, así que aquí está el enlace a su presentación: http://channel9.msdn.com/Events/BUILD/BUILD2011/TOOL-835T Asegúrese de leer los comentarios también :)
fuente
for_each
permítanos implementar el patrón Fork-Join . Aparte de eso, admite una interfaz fluida .patrón de horquilla
Podemos agregar implementación
gpu::for_each
para usar cuda / gpu para computación heterogénea-paralela llamando a la tarea lambda en múltiples trabajadores.Y
gpu::for_each
puede 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.
fuente