Y a-t-il des avantages de la std::for_each
sur- for
boucle? Pour moi, cela std::for_each
semble seulement nuire à la lisibilité du code. Pourquoi alors certaines normes de codage recommandent-elles son utilisation?
c++
stl
foreach
coding-style
manquant
la source
la source
std::for_each
lorsqu'il est utilisé avecboost.lambda
ouboost.bind
peut souvent améliorer la lisibilitéRéponses:
La bonne chose avec C ++ 11 (précédemment appelé C ++ 0x), c'est que ce débat ennuyeux sera réglé.
Je veux dire, personne de sensé, qui souhaite parcourir une collection entière, ne l'utilisera toujours
Ou ca
lorsque la syntaxe de la boucle basée sur
for
la plage est disponible:Ce type de syntaxe est disponible en Java et C # depuis un certain temps maintenant, et en fait, il y a beaucoup plus de
foreach
boucles que defor
boucles classiques dans chaque code Java ou C # récent que j'ai vu.la source
Element & e
commeauto & e
(ouauto const &e
) mieux. J'utiliseraisElement const e
(sans référence) lorsque je veux une conversion implicite, par exemple lorsque la source est une collection de types différents, et que je veux les convertir enElement
.Voici quelques raisons:
Cela semble nuire à la lisibilité simplement parce que vous n'y êtes pas habitué et / ou que vous n'utilisez pas les bons outils pour le rendre vraiment facile. (voir boost :: range et boost :: bind / boost :: lambda pour les helpers. Beaucoup d'entre eux iront dans C ++ 0x et rendront for_each et les fonctions associées plus utiles.)
Il vous permet d'écrire un algorithme au-dessus de for_each qui fonctionne avec n'importe quel itérateur.
Cela réduit le risque de bogues de frappe stupides.
Il ouvre également votre esprit pour le reste de la STL-algorithmes, comme
find_if
,sort
,replace
, etc et ceux - ci ne regardera plus si étrange. Cela peut être une énorme victoire.Mise à jour 1:
Plus important encore, cela vous aide à aller au-delà
for_each
des boucles for comme c'est tout ce qu'il y a, et à regarder les autres STL-alogs, comme find / sort / partition / copy_replace_if, l'exécution parallèle ... ou autre.Un grand nombre de traitements peuvent être écrits de manière très concise en utilisant "le reste" des frères et sœurs de for_each, mais si tout ce que vous faites est d'écrire une boucle for avec diverses logiques internes, vous n'apprendrez jamais à les utiliser, et vous finissent par inventer la roue encore et encore.
Et (le style de plage bientôt disponible for_each):
Ou avec des lambdas C ++ x11:
L'OMI est-elle plus lisible que:
Aussi ceci (ou avec des lambdas, voir autres):
Est plus concis que:
Surtout si vous avez plusieurs fonctions à appeler dans l'ordre ... mais peut-être que ce n'est que moi. ;)
Mise à jour 2 : J'ai écrit mes propres wrappers à une ligne de stl-algos qui fonctionnent avec des plages au lieu de paires d'itérateurs. boost :: range_ex, une fois publié, inclura-t-il cela et peut-être sera-t-il également présent dans C ++ 0x?
la source
outer_class::inner_class::iterator
ou ce sont des arguments de modèle:typename std::vector<T>::iterator
... la construction for elle-même peut sefor_each
dans le deuxième exemple est incorrect (devrait êtrefor_each( bananas.begin(), bananas.end(),...
for_each
est plus générique. Vous pouvez l'utiliser pour itérer sur n'importe quel type de conteneur (en passant les itérateurs de début / fin). Vous pouvez potentiellement échanger des conteneurs sous une fonction qui utilisefor_each
sans avoir à mettre à jour le code d'itération. Vous devez considérer qu'il existe d'autres conteneurs dans le monde que lesstd::vector
anciens tableaux C simples pour voir les avantages defor_each
.L'inconvénient majeur de
for_each
est qu'il faut un foncteur, donc la syntaxe est maladroite. Ceci est corrigé dans C ++ 11 (anciennement C ++ 0x) avec l'introduction de lambdas:Cela ne vous semblera pas bizarre dans 3 ans.
la source
for ( int v : int_vector ) {
(même si elle peut être simulée aujourd'hui avec BOOST_FOREACH)std::for_each(container, [](int& i){ ... });
. Je veux dire pourquoi on est obligé d'écrire un conteneur deux fois?container.each { ... }
sans mentionner les itérateurs de début et de fin. Je trouve un peu redondant que je doive spécifier l'itérateur de fin tout le temps.Personnellement, chaque fois que je devrais faire tout mon possible pour utiliser
std::for_each
(écrire des foncteurs spéciaux / desboost::lambda
s compliqués ), je trouveBOOST_FOREACH
et C ++ 0x basé sur la plage pour plus de clarté:contre
la source
c'est très subjectif, certains diront que l'utilisation
for_each
rendra le code plus lisible, car il permet de traiter différentes collections avec les mêmes conventions.for_each
itslef est implémenté comme une bouclec'est donc à vous de choisir ce qui vous convient.
la source
Comme beaucoup de fonctions de l'algorithme, une première réaction est de penser qu'il est plus illisible d'utiliser foreach qu'une boucle. Cela a été un sujet de nombreuses guerres de flammes.
Une fois que vous vous êtes habitué à l'idiome, vous pouvez le trouver utile. Un avantage évident est qu'il oblige le codeur à séparer le contenu interne de la boucle de la fonctionnalité d'itération réelle. (OK, je pense que c'est un avantage. D'autres disent que vous êtes juste en train de découper le code sans réel avantage).
Un autre avantage est que lorsque je vois foreach, je sais que chaque élément sera traité ou une exception sera levée.
Une boucle for permet plusieurs options pour terminer la boucle. Vous pouvez laisser la boucle suivre son cours complet, ou vous pouvez utiliser le mot-clé break pour sauter explicitement hors de la boucle, ou utiliser le mot-clé return pour quitter toute la fonction à mi-boucle. En revanche, foreach n'autorise pas ces options, ce qui le rend plus lisible. Vous pouvez simplement regarder le nom de la fonction et vous connaissez la nature complète de l'itération.
Voici un exemple de boucle for déroutante :
la source
std::for_each()
dans l'ancien standard (celui de l'époque de ce post) vous devez utiliser un foncteur nommé, qui encourage la lisibilité comme vous le dites et interdit de sortir prématurément de la boucle. Mais alors lafor
boucle équivalente n'a rien d'autre qu'un appel de fonction, et cela aussi interdit une rupture prématurée. Mais à part cela, je pense que vous avez fait un excellent argument en disant que celastd::for_each()
impose de passer par toute la gamme.Vous avez généralement raison: la plupart du temps,
std::for_each
c'est une perte nette. J'irais jusqu'à comparerfor_each
àgoto
.goto
fournit le contrôle de flux le plus polyvalent possible - vous pouvez l'utiliser pour implémenter pratiquement toute autre structure de contrôle que vous pouvez imaginer. Cette polyvalence même, cependant, signifie que voir ungoto
isolé ne vous dit pratiquement rien sur ce qu'il est censé faire dans cette situation. En conséquence, presque personne saine d'esprit ne l'utilisegoto
qu'en dernier recours.Parmi les algorithmes standard,
for_each
c'est à peu près la même chose - il peut être utilisé pour implémenter pratiquement n'importe quoi, ce qui signifie que voir nefor_each
vous dit pratiquement rien sur ce à quoi il est utilisé dans cette situation. Malheureusement, l'attitude des gens à l'égard de la questionfor_each
est de savoir où était leur attitude enversgoto
(disons) 1970 environ - quelques personnes avaient compris qu'il ne devrait être utilisé qu'en dernier recours, mais beaucoup le considèrent toujours comme l'algorithme principal, et rarement, voire jamais, en utiliser d’autre. La grande majorité du temps, même un rapide coup d'œil révélerait que l'une des alternatives était radicalement supérieure.Par exemple, je suis à peu près sûr que j'ai perdu la trace du nombre de fois où j'ai vu des gens écrire du code pour imprimer le contenu d'une collection en utilisant
for_each
. D'après les articles que j'ai vus, cela pourrait bien être l'utilisation la plus courante defor_each
. Ils se retrouvent avec quelque chose comme:Et leur poste demande à ce que la combinaison de
bind1st
,mem_fun
etc. ils ont besoin de faire quelque chose comme:work et imprimez les éléments de
coll
. Si cela fonctionnait vraiment exactement comme je l'ai écrit là-bas, ce serait médiocre, mais ce n'est pas le cas - et au moment où vous l'avez fait fonctionner, il est difficile de trouver ces quelques morceaux de code liés à ce qui passe parmi les pièces qui le maintiennent ensemble.Heureusement, il existe un bien meilleur moyen. Ajoutez une surcharge d'insertion de flux normale pour XXX:
et utilisez
std::copy
:Cela fonctionne - et ne nécessite pratiquement aucun travail pour comprendre qu'il imprime le contenu de
coll
tostd::cout
.la source
boost::mem_fn(&XXX::print)
plutôt queXXX::print
std::cout
comme argument pour que cela fonctionne).L'avantage d'écrire fonctionnel pour être plus lisible, peut ne pas apparaître quand
for(...)
etfor_each(...
).Si vous utilisez tous les algorithmes de Functional.h, au lieu d'utiliser des boucles for, le code devient beaucoup plus lisible;
est beaucoup plus lisible que;
Et c'est ce que je trouve si gentil, généraliser les boucles for à une ligne fonctions =)
la source
Facile:
for_each
est utile lorsque vous avez déjà une fonction pour gérer chaque élément du tableau, vous n'avez donc pas à écrire un lambda. Certainement, ceest mieux que
De plus, la
for
boucle à distance n'itère que sur des conteneurs entiers du début à la fin, tout enfor_each
étant plus flexible.la source
La
for_each
boucle a pour but de masquer les itérateurs (détail de l'implémentation d'une boucle) du code utilisateur et de définir une sémantique claire sur l'opération: chaque élément sera itéré exactement une fois.Le problème de lisibilité dans la norme actuelle est qu'il nécessite un foncteur comme dernier argument au lieu d'un bloc de code, donc dans de nombreux cas, vous devez écrire un type de foncteur spécifique pour celui-ci. Cela se transforme en code moins lisible car les objets foncteurs ne peuvent pas être définis sur place (les classes locales définies dans une fonction ne peuvent pas être utilisées comme arguments de modèle) et l'implémentation de la boucle doit être éloignée de la boucle réelle.
Notez que si vous souhaitez effectuer une opération spécifique sur chaque objet, vous pouvez utiliser
std::mem_fn
, ouboost::bind
(std::bind
dans la norme suivante), ouboost::lambda
(lambdas dans la norme suivante) pour le rendre plus simple:Ce qui n'est pas moins lisible et plus compact que la version roulée à la main si vous avez une fonction / méthode à appeler en place. L'implémentation pourrait fournir d'autres implémentations de la
for_each
boucle (pensez au traitement parallèle).La norme à venir s'occupe de certaines des lacunes de différentes manières, elle autorisera des classes définies localement comme arguments dans les modèles:
Amélioration de la localité du code: lorsque vous naviguez, vous voyez ce qu'il fait ici. En fait, vous n'avez même pas besoin d'utiliser la syntaxe de classe pour définir le foncteur, mais utilisez un lambda juste là:
Même si pour le cas
for_each
il y aura une construction spécifique qui la rendra plus naturelle:J'ai tendance à mélanger la
for_each
construction avec des boucles roulées à la main. Quand seul un appel à une fonction ou une méthode existante est ce dont j'ai besoin (for_each( v.begin(), v.end(), boost::bind( &Type::update, _1 ) )
), je choisis lafor_each
construction qui enlève au code beaucoup de trucs d'itérateur de plaque chauffante. Quand j'ai besoin de quelque chose de plus complexe et que je ne peux pas implémenter un foncteur juste quelques lignes au-dessus de l'utilisation réelle, je roule ma propre boucle (maintient l'opération en place). Dans les sections non critiques du code, je pourrais utiliser BOOST_FOREACH (un collègue m'a mis dedans)la source
Outre la lisibilité et les performances, l'un des aspects souvent négligés est la cohérence. Il existe de nombreuses façons d'implémenter une boucle for (ou while) sur les itérateurs, à partir de:
à:
avec de nombreux exemples entre les deux à différents niveaux d'efficacité et de potentiel de bogue.
Cependant, l'utilisation de for_each renforce la cohérence en supprimant la boucle:
La seule chose dont vous devez vous soucier maintenant est la suivante: implémentez-vous le corps de la boucle en tant que fonction, foncteur ou lambda en utilisant les fonctionnalités Boost ou C ++ 0x? Personnellement, je préfère m'inquiéter de cela que de savoir comment implémenter ou lire une boucle for / while aléatoire.
la source
J'avais l'habitude de ne pas aimer
std::for_each
et je pensais que sans lambda, c'était complètement faux. Cependant, j'ai changé d'avis il y a quelque temps, et maintenant j'adore ça. Et je pense que cela améliore même la lisibilité et facilite le test de votre code de manière TDD.L'
std::for_each
algorithme peut être lu comme quelque chose avec tous les éléments à portée , ce qui peut améliorer la lisibilité. Supposons que l'action que vous souhaitez effectuer dure 20 lignes et que la fonction dans laquelle l'action est exécutée est également d'environ 20 lignes. Cela rendrait une fonction de 40 lignes avec une boucle for classique, et seulement environ 20 avecstd::for_each
, donc probablement plus facile à comprendre.Les fonctions pour
std::for_each
sont plus susceptibles d'être plus génériques, et donc réutilisables, par exemple:Et dans le code, vous n'auriez qu'une seule ligne comme
std::for_each(v.begin(), v.end(), DeleteElement())
ce qui est légèrement mieux IMO qu'une boucle explicite.Tous ces foncteurs sont normalement plus faciles à obtenir dans les tests unitaires qu'une boucle for explicite au milieu d'une longue fonction, et cela seul est déjà une grande victoire pour moi.
std::for_each
est également généralement plus fiable, car vous êtes moins susceptible de faire une erreur avec la portée.Et enfin, le compilateur peut produire un code légèrement meilleur pour
std::for_each
que pour certains types de boucle for artisanale, car il (for_each) a toujours la même apparence pour le compilateur, et les rédacteurs du compilateur peuvent mettre toutes leurs connaissances, pour le rendre aussi bon qu'ils pouvez.Il en va de même pour d'autres algorithmes std comme
find_if
,transform
etc.la source
for
est pour la boucle qui peut itérer chaque élément ou tous les tiers, etc.for_each
est pour itérer seulement chaque élément. Il ressort clairement de son nom. Ce que vous comptez faire dans votre code est donc plus clair.la source
++
. Inhabituel peut-être, mais une boucle for fait de même.transform
pour ne pas confondre quelqu'un.Si vous utilisez fréquemment d'autres algorithmes de la STL, il existe plusieurs avantages à
for_each
:Contrairement à une boucle for traditionnelle,
for_each
vous oblige à écrire du code qui fonctionnera pour n'importe quel itérateur d'entrée. Être restreint de cette manière peut en fait être une bonne chose car:for_each
.L'utilisation
for_each
rend parfois plus évidente que vous pouvez utiliser une fonction STL plus spécifique pour faire la même chose. (Comme dans l'exemple de Jerry Coffin; ce n'est pas nécessairement le cas quifor_each
est la meilleure option, mais une boucle for n'est pas la seule alternative.)la source
Avec C ++ 11 et deux modèles simples, vous pouvez écrire
en remplacement de
for_each
ou d'une boucle. Pourquoi le choisir se résume à la brièveté et à la sécurité, il n'y a aucune chance d'erreur dans une expression qui n'est pas là.Pour moi,
for_each
c'était toujours mieux sur les mêmes bases lorsque le corps de la boucle est déjà un foncteur, et je vais profiter de tous les avantages que je peux obtenir.Vous utilisez toujours les trois expressions
for
, mais maintenant, quand vous en voyez une, vous savez qu'il y a quelque chose à comprendre là-bas, ce n'est pas passe-partout. Je déteste le passe- partout. J'en veux à son existence. Ce n'est pas du vrai code, il n'y a rien à apprendre en le lisant, c'est juste une chose de plus à vérifier. L'effort mental peut être mesuré par la facilité avec laquelle il est de se rouiller en le vérifiant.Les modèles sont
la source
La plupart du temps, vous devrez parcourir toute la collection . Par conséquent, je vous suggère d'écrire votre propre variante for_each (), en ne prenant que 2 paramètres. Cela vous permettra de réécrire l'exemple de Terry Mahaffey comme suit :
Je pense que c'est en effet plus lisible qu'une boucle for. Cependant, cela nécessite les extensions du compilateur C ++ 0x.
la source
Je trouve que for_each est mauvais pour la lisibilité. Le concept est bon, mais c ++ rend très difficile l'écriture lisible, du moins pour moi. Les expressions lamda c ++ 0x seront utiles. J'aime vraiment l'idée des lamdas. Cependant, à première vue, je pense que la syntaxe est très moche et je ne suis pas sûr à 100% de m'y habituer. Peut-être que dans 5 ans, je me serai habitué et je n'y réfléchirai pas une seconde fois, mais peut-être pas. Le temps nous le dira :)
Je préfère utiliser
Je trouve une boucle for explicite plus claire à lire et explicite en utilisant des variables nommées pour les itérateurs de début et de fin réduit l'encombrement dans la boucle for.
Bien sûr, les cas varient, c'est exactement ce que je trouve généralement le mieux.
la source
Vous pouvez faire en sorte que l'itérateur soit un appel à une fonction exécutée à chaque itération de la boucle.
Voir ici: http://www.cplusplus.com/reference/algorithm/for_each/
la source
for_each
fait, auquel cas, cela ne répond pas à la question sur ses avantages.La boucle For peut se rompre; Je ne veux pas être un perroquet pour Herb Sutter alors voici le lien vers sa présentation: http://channel9.msdn.com/Events/BUILD/BUILD2011/TOOL-835T Assurez-vous de lire aussi les commentaires :)
la source
for_each
nous permettent d'implémenter le modèle Fork-Join . Autre que cela, il prend en charge l' interface fluide .motif de jointure en fourche
Nous pouvons ajouter une implémentation
gpu::for_each
pour utiliser cuda / gpu pour le calcul parallèle hétérogène en appelant la tâche lambda dans plusieurs nœuds de calcul.Et
gpu::for_each
peut attendre que les travailleurs travaillent sur toutes les tâches lambda avant d'exécuter les instructions suivantes.interface fluide
Cela nous permet d'écrire du code lisible par l'homme de manière concise.
la source