Avantages de std::for_each plus pour la boucle
Sont les avantages de std::for_each
sur for
boucle? Pour moi, std::for_each
ne semble entraver la lisibilité du code. Pourquoi ne puis certaines normes de codage de recommander son utilisation?
std::for_each
lorsqu'il est utilisé avecboost.lambda
ouboost.bind
peut souvent améliorer la lisibilité- La question et a accepté de répondre sont à partir de 2010. Pour les plus up-to-date de la réponse (à partir de 2018), voir ici:fluentcpp.com/2018/03/30/is-stdfor_each-obsolete
Vous devez vous connecter pour publier un commentaire.
La bonne chose avec C++11 (précédemment appelé C++0x), c'est que cette fastidieuse débat sera réglé.
Je veux dire, personne dans leur bon esprit, qui veut effectuer une itération sur une collection entière, va continuer à utiliser cette
Ou ce
lorsque le gamme à base de
for
boucle syntaxe est disponible:Ce genre de syntaxe a été disponible en Java et C# pour un certain temps maintenant, et effectivement il y a des façon plus
foreach
boucles de classiquefor
boucles dans toutes les plus récentes de Java ou C# code, j'ai vu.Element & e
doit êtreconst Element & e
Element & e
commeauto & e
(ouauto const &e
) semble meilleure. Je voudrais utiliserElement const e
(sans référence) quand je veux conversion implicite, par exemple lorsque la source est une collection de différents types, et je veux les convertir enElement
.Element&
: dur-type,auto
et cv-qualification ne sont pas triviales à décider dans cette construction. c.f. l'explication par Stephan Lavavej isocpp.org/blog/2014/01/n3853std::
fonctionnalités (sauf si c'estfor (auto& e : boost::make_iterator_range(begin, middle) { ... }
), alors que vous pouvez le faire avecstd::for_each(begin, middle, ...)
.Voici quelques raisons:
Il semble faire obstacle à la lisibilité juste parce que vous n'êtes pas habitué, et/ou n'utilisez pas les bons outils autour de lui pour le rendre vraiment facile. (voir boost::gamme et boost::bind/boost::lambda pour les aidants. Beaucoup de ces dans C++0x et faire for_each et d'autres fonctions plus utiles.)
Il vous permet d'écrire un algorithme sur le dessus de for_each qui fonctionne avec tous les itérateur.
Il réduit les chances d'stupide taper des bugs.
Il ouvre également votre esprit pour le reste de la STL-algorithmes, comme
find_if
,sort
,replace
, etc, et ceux-ci n'est pas si étrange plus. Cela peut être une grande victoire.Mise à jour 1:
Plus important encore, il vous permet d'aller au-delà de
for_each
vs pour des boucles comme ça tout y est, et de regarder les autres, STL-alogs, comme trouver /tri /partition /copy_replace_if, parallèle de l'exécution .. ou quoi que ce soit.Beaucoup de traitement peut être écrit de façon très concise en utilisant le "reste" de for_each frères et sœurs, mais si tout ce que vous faire est d'écrire une boucle for avec diverses logique interne, alors vous ne serez jamais apprendre à utiliser, et vous finirez par inventer la roue au-dessus et plus.
Et (le bientôt-à-être une gamme disponible de style for_each):
Ou en C++x11 lambdas:
est de l'OMI, plus lisible que:
Aussi cette (ou avec des lambdas, voir les autres):
Est plus concis que:
Surtout si vous avez plusieurs fonctions à appeler dans l'ordre... mais peut-être que c'est juste moi. 😉
Mise à jour 2: j'ai écrit mon propre one-liner wrappers de stl-algos qui travaillent avec des plages au lieu de la paire d'itérateurs. boost::range_ex, une fois libéré, comprendra et peut-être qu'il sera là dans C++0x trop?
outer_class::inner_class::iterator
ou ils sont les arguments de modèle:typename std::vector<T>::iterator
... pour la construire lui-même pouvez exécuter dans un de nombreux la ligne de construction en elle-mêmefor_each
dans le deuxième exemple est incorrect (ce qui devrait êtrefor_each( bananas.begin(), bananas.end(),...
for_each
est plus générique. Vous pouvez l'utiliser pour effectuer une itération sur tout type de conteneur (en passant par le point de début/fin d'itérateurs). Vous pouvez potentiellement d'échanger des contenants en dessous d'une fonction qui utilisefor_each
sans avoir à mettre à jour l'itération code. Vous devez considérer qu'il y a d'autres conteneurs dans le monde questd::vector
et de la plaine de vieux C des tableaux pour voir les avantages defor_each
.L'inconvénient majeur de
for_each
est qu'il faut un foncteur, donc la syntaxe est maladroit. C'est corrigé dans C++0x avec l'introduction de lambdas:Ce ne sera pas l'air étrange à vous dans 3 ans.
for ( int v : int_vector ) {
(même si il peut être simulé aujourd'hui avec BOOST_FOREACH)std::for_each(container, [](int& i){ ... });
. Je veux dire, pourquoi est-on forcé d'écrire contenant deux fois?[begin, end)
est spécifié àstd::for_each
en donnant labegin
et laend
itérateurs.container.each { ... }
sans mentionner les itérateurs de début et de fin. Je trouve ça un peu redondant que j'ai pour indiquer la fin d'itérateur de tous les temps.Personnellement, à chaque fois que j'avais besoin de sortir de ma façon d'utiliser
std::for_each
(écrire à des fins spéciales de foncteurs /compliquéboost::lambda
s), je trouveBOOST_FOREACH
et C++0x de gamme à base de mieux:vs
son très subjectif, certains diront que l'utilisation de
for_each
vais faire le code plus lisible, car il permet de traiter les différentes collections avec les mêmes conventions.for_each
itslef est mis en œuvre comme une boucledonc à vous de choisir ce qui est bon pour vous.
Vous êtes la plupart du temps correct: la plupart du temps,
std::for_each
est une perte nette. J'irais même jusqu'à comparerfor_each
àgoto
.goto
fournit le plus polyvalent de contrôle de flux possible, vous pouvez l'utiliser pour mettre en œuvre pratiquement toute autre structure de contrôle que vous pouvez imaginer. Que très polyvalence, cependant, signifie que de voir ungoto
dans l'isolement, vous indique pratiquement rien sur ce qu'il est destiné à faire dans cette situation. En conséquence, presque personne dans leur bon esprit utilisegoto
, sauf en dernier recours.Parmi les algorithmes standard,
for_each
est beaucoup de la même manière, il peut être utilisé pour mettre en œuvre pratiquement rien, ce qui signifie que le fait de voirfor_each
vous dit pratiquement rien de ce qu'il est utilisé dans cette situation. Malheureusement, l'attitude de la population enversfor_each
est au sujet de leur attitude à l'égard degoto
était de (disons) de 1970, ou alors, un quelques les gens avaient attrapé sur le fait qu'il devrait être utilisé seulement comme un dernier recours, mais beaucoup considèrent encore la principale de l'algorithme, et rarement, sinon jamais utiliser n'importe quel autre. La grande majorité du temps, même un rapide coup d'œil a révélé que l'une des solutions de rechange a été considérablement supérieur.Juste pour exemple, je suis sûr que j'ai perdu la trace de combien de fois j'ai vu des gens de l'écriture de code pour imprimer le contenu d'une collection à l'aide de
for_each
. Basé sur des postes que j'ai vu, c'est peut être le seul usage le plus courant defor_each
. Ils se retrouvent avec quelque chose comme:Et leur post est de demander à propos de la combinaison de
bind1st
,mem_fun
, etc. ils ont besoin de faire quelque chose comme:de travail, et d'imprimer les éléments de
coll
. Si il a vraiment fait un travail exactement comme je l'ai écrit il y a, il serait médiocre, mais il n'est pas -- et par le temps que vous avez eu à le faire fonctionner, il est difficile d'en trouver quelques bouts de code lié à ce qui se passe parmi les morceaux qui tiennent ensemble.Heureusement, il ya une bien meilleure façon. Ajouter un flux normal insertion de surcharge pour XXX:
et l'utilisation
std::copy
:Qui ne fonctionne pas -- et prend pratiquement pas de travail à tous de comprendre qu'il imprime le contenu de
coll
àstd::cout
.boost::mem_fn(&XXX::print)
plutôt queXXX::print
std::cout
comme argument pour que cela fonctionne).Comme beaucoup de l'algorithme de fonctions, une première réaction est de penser que c'est plus illisible pour utiliser foreach qu'une boucle. C'est un sujet de beaucoup de guerres de flamme.
Une fois que vous vous habituez à l'idiome vous trouverez peut-être utile. Un avantage évident est qu'il oblige le programmeur à séparer les composants internes de la boucle à partir de l'itération réelle fonctionnalité. (OK, je pense que c'est un avantage. Les autres disent que vous êtes juste de découper le code avec aucune autre prestation en nature).
Un autre avantage est que quand je vois foreach, je savoir que chaque élément sera traité ou une exception sera levée.
Un pour boucle permet plusieurs options pour la terminaison de la boucle. Vous pouvez laisser la boucle exécuter dans son intégralité, ou vous pouvez utiliser le break mot clé explicitement sauter hors de la boucle, ou utiliser le retour mot clé pour sortir de l'ensemble de la fonction mi-boucle. En revanche, foreach ne permet pas à ces options, et cela le rend plus lisible. Vous pouvez simplement coup d'œil sur le nom de la fonction et que vous connaissez la nature de l'itération.
Voici un exemple de confusion pour boucle:
std::for_each()
dans l'ancienne norme (celle de l'époque de ce post), vous devez utiliser un nom de foncteur, qui encourage la lisibilité comme vous dites, et interdit de sortir de la boucle prématurément. Mais alors l'équivalentfor
boucle n'a rien, mais un appel de fonction, et que lui aussi interdit de sortir prématurément. Mais d'autres que je pense que vous avez fait un excellent point en disant questd::for_each()
applique en passant par toute la gamme.L'avantage de l'écriture fonctionnelle pour être plus lisible, pourrait ne pas se montrer quand
for(...)
etfor_each(...
).Si vous utilisez tous les algorithmes fonctionnels.h, au lieu de l'utiliser pour des boucles, le code est beaucoup plus lisible;
est beaucoup plus lisible qu';
Et c'est ce que je pense, c'est tellement agréable, de généraliser les pour-boucles à l'une des fonctions de ligne =)
Facile:
for_each
est utile lorsque vous avez déjà une fonction pour gérer chaque élément de tableau, de sorte que vous n'avez pas à écrire un lambda. Certes, ceest mieux que
Également, à distance
for
boucle d'itération porte uniquement sur l'ensemble des conteneurs à partir de début à la fin, tandis quefor_each
est plus souple.La
for_each
boucle est destinée à cacher les itérateurs (détail de la façon dont une boucle est mise en œuvre) à partir du code d'utilisateur et de définir clairement la sémantique de l'opération: chaque élément sera réitéré exactement une fois.Le problème de la lisibilité dans le standard actuel est qu'il nécessite un foncteur comme le dernier argument au lieu d'un bloc de code, de sorte que dans de nombreux cas, vous devez écrire des foncteur type pour elle. Que devient moins lisible le code comme foncteur objets ne peuvent pas être définis en place (local classes définies à l'intérieur d'une fonction ne peuvent pas être utilisés comme arguments de modèle) et la mise en œuvre de la boucle doit être éloigné de la boucle.
Noter que si vous souhaitez effectuer une opération spécifique sur chaque objet, vous pouvez utiliser
std::mem_fn
, ouboost::bind
(std::bind
dans le prochain standard), ouboost::lambda
(lambdas dans le prochain standard) pour faire plus simple:Qui n'est pas moins lisible et plus compact que le roulé à la main de version si vous avez de la fonction/méthode à appeler en place. La mise en œuvre pourrait fournir d'autres implémentations de la
for_each
boucle (pensez à traitement parallèle).La norme à venir prend soin de certaines lacunes de différentes manières, il permettra localement les classes définies comme des arguments de modèles:
L'amélioration de la localité de code: lorsque vous naviguez, vous voyez ce qu'il fait là. Comme une question de fait, vous n'avez même pas besoin d'utiliser la classe syntaxe pour définir le foncteur, mais d'utiliser une lambda là:
Même si, pour le cas de
for_each
il y aura un spécifique de la construction qui va la rendre plus naturelle:J'ai tendance à mélanger les
for_each
à construire avec les roulés à la main boucles. Lorsque seulement un appel à une fonction ou une méthode est ce dont j'ai besoin (for_each( v.begin(), v.end(), boost::bind( &Type::update, _1 ) )
) je vais pour lefor_each
construire qui n'est pas le code d'un lot de chaudière plaque itérateur choses. Quand j'ai besoin de quelque chose de plus complexe et je ne peut pas mettre en œuvre un foncteur juste un couple de lignes au-dessus de l'utilisation réelle, je roule ma propre boucle (qui garde l'opération en place). En non-critique des sections de code, je pourrais aller avec BOOST_FOREACH (un collègue m'a fait ça)Côté de la lisibilité et de la performance, un aspect souvent négligé est la cohérence. Il existe de nombreuses façons de mettre en œuvre une (ou tout) de la boucle sur les itérateurs, à partir de:
à:
avec de nombreux exemples, entre les divers niveaux d'efficacité et de bug potentiel.
À l'aide de for_each, cependant, veille à la cohérence par l'abstraction de la boucle:
La seule chose que vous avez à vous soucier est maintenant: pensez-vous mettre en œuvre le corps de la boucle tant que fonction, un foncteur, ou un lambda à l'aide de Boost ou de C++0x fonctionnalités? Personnellement, je préfère vous inquiétez à ce sujet que la façon de mettre en œuvre ou de lecture aléatoire pour/boucle while.
J'ai utilisé le dégoût
std::for_each
et de la pensée que, sans lambda, il a été fait absolument mauvais. Cependant, j'ai fait changer mon esprit il y a quelques temps, et maintenant je fait l'amour il. Et je pense que c'-même améliore la lisibilité et la rend plus facile à tester votre code dans un TDD façon.La
std::for_each
algorithme peut être lu comme faire quelque chose avec tous les éléments de la gamme, qui peut d'améliorer la lisibilité. Dire que l'action que vous voulez effectuer est de 20 lignes de long, et la fonction où l'action est réalisée est également à environ 20 lignes de long. Que ferait une fonction 40 lignes de long, avec un classique de la boucle, et seulement environ 20 àstd::for_each
, donc probablement plus facile à comprendre.Foncteurs pour
std::for_each
sont plus susceptibles d'être plus générique, et donc réutilisables, e.g:Et dans le code que tu avais seulement un one-liner comme
std::for_each(v.begin(), v.end(), DeleteElement())
ce qui est légèrement mieux IMO qu'explicite d'une boucle.Toutes ces foncteurs sont normalement plus facile à obtenir en vertu des tests unitaires qu'explicite pour la boucle dans le milieu d'une longue fonction, et que lui seul est déjà une grande victoire pour moi.
std::for_each
est généralement plus fiable, que vous êtes moins susceptibles de faire une erreur avec la gamme.Et enfin, le compilateur peut produire un code un peu meilleur pour
std::for_each
que pour certains types de main-conçu pour la boucle, comme il (for_each) toujours regarde la même chose pour le compilateur, et les rédacteurs de compilateur peut mettre l'ensemble de leurs connaissances, pour le rendre aussi bon que possible.Même chose s'applique à d'autres mst algorithmes comme
find_if
,transform
etc.for
est pour la boucle peut parcourir chaque élément ou chaque troisième, etc.for_each
est pour réitérer à chaque élément. Il est clair à partir de son nom. Il est donc plus claire de ce que vous avez l'intention de faire dans votre code.++
. Inhabituel, peut-être, mais c'est une boucle à faire de même.transform
à ne pas confondre quelqu'un.Si vous utilisez fréquemment d'autres algorithmes de la STL, il y a plusieurs avantages à
for_each
:Contrairement aux entreprises traditionnelles pour la boucle,
for_each
vous oblige à écrire le code qui va travailler pour un itérateur d'entrée. La restriction de cette manière peut effectivement être une bonne chose parce que:for_each
.À l'aide de
for_each
rend parfois plus évident que vous pouvez utiliser un plus spécifiques à la fonction STL pour faire la même chose. (Comme dans Jerry Cercueil de l'exemple; il n'est pas nécessairement le cas quefor_each
est la meilleure option, mais une boucle for n'est pas la seule alternative.)Avec C++11 et simplement les deux modèles, vous pouvez écrire
comme un remplacement pour
for_each
ou une boucle. Pourquoi choisir elle se résume à de la brièveté et de la sécurité, il n'y a aucune chance d'erreur dans l'une expression qui n'y est pas.Pour moi,
for_each
était toujours mieux sur le même terrain lorsque le corps de la boucle est déjà un foncteur, et je vais prendre un avantage que je peux obtenir.Vous devez toujours utiliser les trois-expression
for
, mais maintenant, quand vous le verrez, vous savez qu'il y a quelque chose à comprendre là, c'est pas réutilisable. Je haine standard. Je lui en veux de son existence. Ce n'est pas vrai code, il n'y a rien à apprendre en lisant ça, c'est juste une chose de plus que les besoins de la vérification. L'effort mental peut être mesuré par la façon dont il est facile d'obtenir rouillé pour vérification.Les modèles sont
Plupart du temps, vous aurez à itérer sur l'ensemble de la collection. Par conséquent, je vous suggère d'écrire votre propre for_each() de la variante, en ne prenant que 2 paramètres. Cela va vous permettre de réécrire Terry Mahaffey l'exemple de comme:
Je pense que c'est en effet plus lisible qu'une boucle for. Cependant, cela nécessite le C++0x compilateur extensions.
Je trouve for_each pour être mauvais pour la lisibilité. Le concept est bon mais le c++ rend très difficile l'écriture lisible, au moins pour moi. c++0x lamda expressions de l'aide. J'aime vraiment l'idée de lamdas. Cependant, à première vue, je pense que la syntaxe est très moche et je ne suis pas 100% sûr que je vais m'habituer à elle. Peut-être que dans 5 ans je vais l'avoir utilisé et ne pas donner une seconde pensée, mais peut-être pas. Le temps nous le dira 🙂
Je préfère utiliser
- Je trouver un explicite pour la boucle plus clair à lire et explicitement à l'aide de variables nommées pour le début et la fin des itérateurs réduit l'encombrement dans la boucle for.
Bien sûr les cas varient, c'est juste ce que j'ai l'habitude de trouver mieux.
Vous pouvez avoir l'itérateur être un appel à une fonction qui est exécutée sur chaque itération de la boucle.
Voir ici:
http://www.cplusplus.com/reference/algorithm/for_each/
for_each
n', dans ce cas, il ne répond pas à la question sur ses avantages.Pour la boucle break;
Je ne veux pas être un perroquet pour Herb Sutter voici donc le lien vers sa présentation:
http://channel9.msdn.com/Events/BUILD/BUILD2011/TOOL-835T
Assurez-vous de lire les commentaires aussi 🙂
for_each
nous permettre de mettre en œuvre Fork-Join modèle . Autre que cela, il prend en charge fluent interface.fork-join modèle
Nous pouvons ajouter la mise en œuvre
gpu::for_each
d'utiliser cuda/gpu pour hétérogène-calcul parallèle en appelant le lambda tâche dans plusieurs travailleurs.Et
gpu::for_each
peut attendre pour les travailleurs de travailler sur toutes les lambda-tâches avant la fin de l'exécution de la prochaine consolidés.fluent interface
Il permet d'écrire de l'homme-un code lisible de manière concise.