Ça dépend.
Le pouvoir de for_each
c'est que vous pouvez l'utiliser avec n'importe quel conteneur dont les itérateurs satisfont au concept d'itérateur d'entrée et, en tant que tel, il est utilisable de manière générique sur n'importe quel conteneur. Cela augmente la maintenabilité dans la mesure où vous pouvez simplement changer de conteneur sans avoir à modifier quoi que ce soit. Il n'en va pas de même pour une boucle sur le fichier size
d'un vecteur. Le seul autre conteneur avec lequel vous pourriez l'échanger sans devoir modifier la boucle serait un autre conteneur à accès aléatoire.
Maintenant, si vous tapez vous-même la version de l'itérateur, la version typique ressemble à ceci :
// substitute 'container' with a container of your choice
for(std::container<T>::iterator it = c.begin(); it != c.end(); ++it){
// ....
}
Plutôt long, non ? C++0x nous soulage de ce problème de longueur grâce à la fonction auto
mot-clé :
for(auto it = c.begin(); it != c.end(); ++it){
// ....
}
Déjà plus beau, mais pas encore parfait. Vous appelez end
à chaque itération et cela peut être mieux fait :
for(auto it = c.begin(), ite = c.end(); it != ite; ++it){
// ....
}
Ça a l'air bien maintenant. Quand même, plus long que l'équivalent for_each
version :
std::for_each(c.begin(), c.end(), [&](T& item){
// ...
});
Le terme "équivalent" étant légèrement subjectif, car l T
dans la liste des paramètres de la lambda pourrait être du type verbeux comme my_type<int>::nested_type
. Cependant, on peut typedef
son moyen de contourner cela. Honnêtement, je ne comprends toujours pas pourquoi les lambdas n'ont pas été autorisés à être polymorphes avec la déduction de type...
Maintenant, une autre chose à considérer est que for_each
le nom lui-même exprime déjà une intention. Il indique qu'aucun élément de la séquence ne sera sauté, ce qui pourrait être le cas avec votre boucle for normale.
Cela m'amène à un autre point : Depuis que for_each
est destiné à parcourir l'ensemble de la séquence et à appliquer une opération sur chaque dans le conteneur, il n'est pas conçu pour traiter les premiers return
ou break
en général. continue
peut être simulée avec un return
de l'instruction lambda/facteur.
Donc, utilisez for_each
où vous vraiment vous voulez appliquer une opération sur chaque dans la collection.
En passant, for_each
pourraient être "dépréciées" avec C++0x grâce aux formidables boucles for basées sur la plage (également appelées boucles foreach) :
for(auto& item : container){
// ...
}
Ce qui est beaucoup plus court (yay) et permet les trois options suivantes :
- retour anticipé (même avec une valeur de retour !)
- sortir de la boucle et
- en sautant certains éléments.
2 votes
Si vous n'êtes pas familier avec une fonctionnalité, comment saurez-vous quand il est approprié de l'utiliser ?
1 votes
Définissez "devrait". Les performances seront presque certainement identiques. La portabilité est identique tant que l'on parle de C++0x. La lisibilité/maintenabilité est dans l'œil de celui qui regarde...
8 votes
Je pense qu'un
for_each
+ lambda s'exprime mieux comme un for basé sur une plage :for (int n : v) { cout << n << endl; }
0 votes
@Greg, juste pour que nous soyons sur la même longueur d'onde, la familiarité a des degrés. Par exemple, j'en sais assez sur
for_each
pour écrire la ligne de code ci-dessus, mais je ne connais guère plus. Décider de l'utiliser signifierait que je le googlerais et que j'en apprendrais davantage en essayant d'accomplir une tâche spécifique.0 votes
Pour être clair, je vous encourage à vous renseigner sur les nouvelles fonctionnalités, leurs avantages et leurs inconvénients, ainsi que sur les endroits où il est possible de les utiliser. Alors oui, renseignez-vous !