Les deux peuvent être utilisés pour appliquer une fonction à une gamme d'éléments.
Au plus haut niveau:
std::for_each
ignore la valeur de retour de la fonction et garantit l’ordre d’exécution.std::transform
attribue la valeur de retour à l'itérateur et ne garantit pas l'ordre d'exécution.Quand préférez-vous utiliser l'un par rapport à l'autre? Y at-il des mises en garde subtiles?
std::transform
est identique à map
. L'idée est d'appliquer une fonction à chaque élément entre les deux itérateurs et d'obtenir un conteneur différent composé d'éléments résultant de l'application d'une telle fonction. Vous voudrez peut-être l'utiliser pour, par exemple, projeter le membre de données d'un objet dans un nouveau conteneur. Dans ce qui suit, std::transform
est utilisé pour transformer un conteneur de std::string
s dans un conteneur de std::size_t
s.
std::vector<std::string> names = {"hi", "test", "foo"};
std::vector<std::size_t> name_sizes;
std::transform(names.begin(), names.end(), std::back_inserter(name_sizes), [](const std::string& name) { return name.size();});
Par contre, vous exécutez std::for_each
pour les seuls effets secondaires. En d'autres termes, std::for_each
ressemble beaucoup à une simple boucle for
.
Retour à l'exemple de chaîne:
std::for_each(name_sizes.begin(), name_sizes.end(), [](std::size_t name_size) {
std::cout << name_size << std::endl;
});
En effet, à partir de C++ 11, il est possible d’obtenir la même chose avec une notation en utilisant des boucles for
basées sur des intervalles:
for (std::size_t name_size: name_sizes) {
std::cout << name_size << std::endl;
}
Votre vue d'ensemble de haut niveau
std::for_each
Ignore la valeur de retour de la fonction et garantit l'ordre d'exécution.std::transform
Assigne la valeur de retour à l'itérateur et ne garantit pas l'ordre d'exécution.
à peu près le couvre.
Une autre façon de voir les choses ( préférer l’une à l’autre);
Une dernière chose à garder à l’esprit ( mise en garde subtile) est le changement dans les exigences des opérations de std::transform
avant et après C++ 11 (de en.cppreference.com);
Celles-ci devaient permettre essentiellement l'exécution indéterminée.
Quand dois-je utiliser l'un sur l'autre?
Si je veux manipuler chaque élément d'une plage, j'utilise for_each
. Si je dois calculer quelque chose à partir de chaque élément, j'utiliserais alors transform
. Lorsque vous utilisez for_each
Et transform
, je les couple normalement avec un lambda.
Cela dit, je trouve que mon utilisation actuelle du traditionnel for_each
Est quelque peu amoindrie depuis l'avènement de la boucle basée sur la plage for
et des lambdas en C++ 11 (for (element : range)
) . Je trouve sa syntaxe et son implémentation très naturelles (mais votre kilométrage variera ici) et un ajustement plus intuitif pour certains cas d'utilisation.
Bien que la question ait reçu une réponse, je pense que cet exemple permettrait de clarifier davantage la différence.
for_each
appartient à opérations STL non modificatives, ce qui signifie que ces opérations ne modifient ni les éléments de la collection ni la collection elle-même. Par conséquent, la valeur renvoyée par for_each est toujours ignorée et n'est pas affectée à un élément de la collection. Néanmoins, il est toujours possible de modifier des éléments de collection, par exemple lorsqu'un élément est passé à la fonction f à l'aide de la référence. Il faut éviter un tel comportement, car il n’est pas conforme aux principes du TSL.
En revanche, la fonction transform
appartient à modification des opérations STL et applique les prédicats donnés (unary_op ou binary_op) aux éléments de la collection ou des collections et stocke les résultats dans une autre collection.
#include <vector>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
void printer(int i) {
cout << i << ", ";
}
int main() {
int mynumbers[] = { 1, 2, 3, 4 };
vector<int> v(mynumbers, mynumbers + 4);
for_each(v.begin(), v.end(), negate<int>());//no effect as returned value of UnaryFunction negate() is ignored.
for_each(v.begin(), v.end(), printer); //guarantees order
cout << endl;
transform(v.begin(), v.end(), v.begin(), negate<int>());//negates elements correctly
for_each(v.begin(), v.end(), printer);
return 0;
}
qui va imprimer:
1, 2, 3, 4,
-1, -2, -3, -4,
Exemple concret d'utilisation de std :: tranform: lorsque vous souhaitez convertir une chaîne en majuscule, vous pouvez écrire du code comme celui-ci:
std::transform(s.begin(), s.end(), std::back_inserter(out), ::toupper);
si vous essayez de faire la même chose avec std :: for_each comme:
std::for_each(s.begin(), s.end(), ::toupper);
Il ne le convertira pas en chaîne majuscule