198 votes

Avantages de std::for_each par rapport à la boucle for

Y a-t-il des avantages à std::for_each sur for boucle ? Pour moi, std::for_each ne semble que nuire à la lisibilité du code. Pourquoi alors certaines normes de codage recommandent-elles son utilisation ?

11 votes

std::for_each lorsqu'il est utilisé avec boost.lambda o boost.bind peut souvent améliorer la lisibilité

0 votes

La question et la réponse acceptée datent de 2010. Pour une réponse plus à jour (de 2018), voir ici : fluentcpp.com/2018/03/30/est-stdfor_each-obsolete

201voto

Thomas Petit Points 4100

Ce qui est bien avec C++11 (précédemment appelé C++0x), est que ce débat fastidieux sera réglé.

Je veux dire, aucune personne saine d'esprit, qui veut itérer sur une collection entière, n'utilisera encore ce

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

Ou ceci

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

lorsque le basé sur la gamme for boucle est disponible :

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

Ce type de syntaxe est disponible en Java et en C# depuis un certain temps déjà, et il y a en fait beaucoup plus d'exemples de ce type de syntaxe. foreach boucles que les classiques for dans chaque code Java ou C# récent que j'ai vu.

12 votes

En fait, une boucle foreach avec scoop est disponible depuis longtemps dans boost, et je veux toujours itérer avec for_each et une fonction lambda.

19 votes

L'hypothèse selon laquelle vous souhaitez disposer de l'ensemble de la gamme de conteneurs ne fait pas partie de la question, il ne s'agit donc que d'une réponse partielle.

6 votes

Notez que le fait de boucler sur un élément n'est probablement pas la seule chose que vous voulez faire, donc cela peut être une bonne idée d'utiliser for_each juste pour que vous appreniez à utiliser find / partition / copy_replace_if et les autres, qui sont ce que beaucoup de boucles for font réellement.

61voto

Macke Points 13474

Voici quelques raisons :

  1. Cela semble entraver la lisibilité simplement parce que vous n'y êtes pas habitué et/ou parce que vous n'utilisez pas les bons outils qui l'entourent pour le rendre vraiment facile (voir boost::range et boost::bind/boost::lambda pour les aides). Beaucoup d'entre eux iront dans C++0x et rendront for_each et les fonctions connexes plus utiles).

  2. Il vous permet d'écrire un algorithme au-dessus de for_each qui fonctionne avec n'importe quel itérateur.

  3. Cela réduit les risques de fautes de frappe.

  4. Il ouvre également votre esprit au reste des algorithmes STL, tels que find_if , sort , replace etc. et ils n'auront plus l'air aussi étranges. Cela peut être une grande victoire.

Mise à jour 1 :

Plus important encore, il vous aide à aller au-delà for_each contre les boucles for comme s'il n'y avait que ça, et regardez les autres analogues STL, comme find / sort / partition / copy_replace_if, exécution parallèle ou autre.

Beaucoup de traitements peuvent être écrits de manière très concise en utilisant "le reste" des frères et soeurs de for_each, mais si tout ce que vous faites est d'écrire une boucle for avec diverses logiques internes, alors vous n'apprendrez jamais à les utiliser, et vous finirez par inventer la roue encore et encore.

Et (le style for_each qui sera bientôt disponible) + lambdas :

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

est, selon l'OMI, plus lisible que :

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

Aussi ceci :

for_each(bananas, [&](auto& b) { my_monkey.eat(b); );

est plus concis que :

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

Mais la nouvelle gamme basée pour est probablement la meilleure :

 for (auto& b : bananas)
     my_monkey.eat(b);

Mais le for_each pourrait être utile, surtout si vous avez plusieurs fonctions à appeler dans l'ordre mais que vous devez exécuter chaque méthode pour tous les objets avant la suivante... mais peut-être que ce n'est que moi. ;)

Mise à jour 2 : J'ai écrit mes propres wrappers one-liner de stl-algos qui fonctionnent avec des plages au lieu de paires d'itérateurs. boost::range_ex, une fois publié, inclura cela et peut-être sera-t-il là dans C++0x aussi ?

0 votes

+1, plusieurs fonctions ou types imbriqués : outer_class::inner_class::iterator ou ce sont des arguments de modèle : typename std::vector<T>::iterator ... la construction pour elle-même peut devenir une construction de plusieurs lignes en elle-même.

4 votes

(btw : le for_each dans le deuxième exemple est incorrect (devrait être for_each( bananas.begin(), bananas.end(),...

0 votes

J'ai écrit des wrappers qui utilisent des plages au lieu de deux itérateurs. Ils seront disponibles plus tard (voir range_ex) mais tout le monde devrait les avoir de toute façon. (Ajout d'une mise à jour à ce sujet).

26voto

Terry Mahaffey Points 7368

for_each est plus générique. Vous pouvez l'utiliser pour itérer sur tout type de conteneur (en passant les itérateurs begin/end). Vous pouvez potentiellement intervertir les conteneurs sous une fonction qui utilise la fonction for_each sans avoir à mettre à jour le code d'itération. Vous devez tenir compte du fait qu'il existe d'autres conteneurs dans le monde que le std::vector et de simples tableaux en C pour voir les avantages de for_each .

L'inconvénient majeur de for_each est qu'il prend un foncteur, donc la syntaxe est maladroite. Ce problème est résolu dans C++11 (anciennement C++0x) avec l'introduction des lambdas :

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

Cela ne vous paraîtra pas bizarre dans 3 ans.

2 votes

@Marcus : ce sera la construction "rangée-pour" et la syntaxe ne lira pas "for_each" en soi : for ( int v : int_vector ) { (même si cela peut être simulé aujourd'hui avec BOOST_FOREACH)

0 votes

@David : Je fais référence à l'ajout général de fonctions basées sur des plages (afin que vous puissiez utiliser des plages avec toutes ces fonctions for_each, copy, remove_if, etc etc),

1 votes

Pourquoi n'est-il pas possible d'écrire : std::for_each(container, [](int& i){ ... }); . Je veux dire pourquoi on est obligé d'écrire deux fois le conteneur ?

18voto

UncleBens Points 24580

Personnellement, chaque fois que j'aurais besoin de faire un détour pour utiliser std::for_each (écrire des foncteurs à usage spécial / compliqué boost::lambda s), je trouve BOOST_FOREACH et le C++0x est basé sur la gamme pour plus de clarté :

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

12voto

Alon Points 4010

C'est très subjectif, certains diront qu'utiliser for_each rendra le code plus lisible, car il permet de traiter différentes collections avec les mêmes conventions. for_each sonlef est implémenté comme une boucle

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

C'est donc à vous de choisir ce qui vous convient le mieux.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X