Il me semble manquer un point dans le mécanisme lambda en C++. Voici le code:
std::vector<int> vec (5);
int init = 0;
std::generate(begin(vec), end(vec), [init]() mutable { return ++init; });
for (auto item : vec) {
std::cout << item << " ";
}
std::cout << std::endl << init << std::endl;
S'il n'y a pas de mutable
il ne se compilerait pas car je change init
dans lambda.
Maintenant, si je comprends bien, lambda est appelé pour chaque élément du vecteur avec un nouvelle nouvelle copie de init
qui est 0. Donc, 1 doit être retourné à chaque fois. Mais la sortie de ce morceau de code est:
1 2 3 4 5
Il ressemble à generate
capture par copie init
ne seule fois au début de son exécution. Mais pourquoi? Est-il censé fonctionner comme ça?
Maintenant, si je comprends bien, lambda est appelé pour chaque élément de vecteur avec une nouvelle copie fraîche d'init qui est 0.
Ce n'est pas correct. Un lambda est juste une autre façon de créer une classe et de lui fournir une operator()
. La partie []
Du lambda décrit les variables membres et si elles sont capturées par référence ou par valeur. La partie ()
Du lambda est la liste des paramètres pour la operator()
et la partie {}
Est le corps de cette fonction. La partie mutable
indique au compilateur de rendre la operator()
non const
telle qu'elle est const
par défaut.
Alors
[init]() mutable { return ++init; }
Devient
struct compiler_generated_name
{
int init; // we captured by value
auto operator()() // since we used mutable this is non const
{
return ++init;
}
};
J'ai utilisé une structure ici pour la brièveté du typage mais un lambda est spécifié comme type de classe afin que class
puisse être utilisé.
Cela signifie que le init
est le même init
de la dernière itération que vous n'avez jamais capturé qu'une seule fois. Il est important de se rappeler que
auto generate_lambda()
{
int foo = 0;
return [&foo](){ return ++foo; };
}
Vous laissera une référence pendant à foo
lorsque la fonction revient et que son utilisation est un comportement non défini.
Le lambda est une structure générée par le compilateur équivalente à:
struct lambda
{
int init = 0; // captured value
auto operator()() // non-const, due to `mutable`
{
return ++init;
}
};
Par conséquent, init
est capturé et copié à l'intérieur du lambda une seule fois - appeler le lambda plusieurs fois ne capturera pas à nouveau init
.
Vous faites face et voyez la valeur initiale d'init - ce que vous voulez probablement faire selon ce que vous attendez est de capturer init
par référence .....
std::vector<int> vec (5);
int init = 0;
std::generate(begin(vec), end(vec), [&init]() mutable { return ++init; });
for (auto item : vec) {
std::cout << item << " ";
}
std::cout << std::endl << init << std::endl;
Votre erreur est ici "Maintenant, si je comprends bien, lambda s'appelle pour chaque élément du vecteur avec une nouvelle copie fraîche d'init qui est 0" (mes italiques). Non; comme vous pouvez le voir, le lambda est complètement séparé, et donc ignorant, du code vectoriel. L'initialisation de item
a lieu chaque fois que la forme lambda elle-même est évaluée (par opposition à chaque fois que la valeur résultante est appelée); ici, cela signifie que chaque fois que la fonction generate
est appelée: une seule fois.