Les Lambdas et la capture par référence les variables locales : Accès après la portée
Je transmets mon local des variables par référence à deux lambda. J'appelle ces lambdas en dehors de la portée de la fonction. Est-ce undefined
?
std::pair<std::function<int()>, std::function<int()>> addSome() {
int a = 0, b = 0;
return std::make_pair([&a,&b] {
++a; ++b;
return a+b;
}, [&a, &b] {
return a;
});
}
int main() {
auto f = addSome();
std::cout << f.first() << " " << f.second();
return 0;
}
Si elle n'est pas, cependant, des changements dans l'un lambda ne sont pas reflétées dans d'autres lambda.
Suis-je malentendu par référence dans le contexte des lambdas ?
Je suis en train d'écrire pour les variables et il semble fonctionner correctement sans l'exécution des erreurs avec la sortie
2 0
. Si cela fonctionne, alors je m'attends à la sortie 2 1
.
OriginalL'auteur Ashish Negi | 2015-01-05
Vous devez vous connecter pour publier un commentaire.
Oui, cela provoque un comportement indéfini. Les lambdas référence de pile, les objets alloués qui sont allés hors de portée. (Techniquement, ce que je comprends, le comportement est défini jusqu'à ce que les lambdas accès
a
et/oub
. Si vous n'avez jamais invoquer le retour de l'lambdas alors il n'y a pas d'AC.)C'est un comportement indéfini de la même façon que c'est un comportement indéfini pour renvoyer une référence à une pile alloué local et ensuite utiliser cette référence une fois que le local est hors de portée, sauf que dans ce cas, il est dissimulé un peu par le lambda.
En outre, notez que l'ordre dans lequel les lambdas sont invoqués n'est pas spécifié -- le compilateur est libre d'invoquer
f.second()
avantf.first()
parce que les deux font partie de la même expression. Par conséquent, même si nous fixons le comportement non défini causés par l'utilisation de références à détruit des objets, à la fois2 0
et2 1
sont encore valide résultats de ce programme, et qui dépend de l'ordre dans lequel votre compilateur décide d'exécuter les lambdas. Notez que c'est pas un comportement indéfini, car le compilateur ne peut pas faire quoi que ce soit, plutôt il a simplement un peu de liberté pour décider de la ordre dans lequel faire quelques choses.(Gardez à l'esprit que
<<
dans votremain()
fonction invoque une coutumeoperator<<
fonction, et l'ordre dans lequel les arguments de la fonction sont évalués n'est pas spécifié. Les compilateurs sont libres d'émettre un code qui évalue tous les arguments de la fonction au sein de la même expression dans n'importe quel ordre, avec la contrainte que tous les arguments d'une fonction doit être évaluée avant que la fonction est appelée.)Pour résoudre le premier problème, utilisez
std::shared_ptr
pour créer une référence compté objet. La Capture de ce pointeur partagé par valeur, et les lambdas gardera le-pointu pour objet vivant aussi longtemps qu'ils (et toutes les copies de celui-ci) existent. Cette allouées sur la pile d'objet est l'endroit où nous allons stocker l'état partagé dea
etb
.Pour résoudre le deuxième problème, évaluer chaque lambda dans une déclaration séparée.
Voici votre code réécrit avec le comportement non défini fixe, et avec
f.first()
garanti d'être invoqués devantf.second()
:(Voir courir.)
OriginalL'auteur cdhowie
Malheureusement, C++ lambdas peuvent capturer par référence, mais ne permettent pas de résoudre le "la hausse funarg problème".
Faire il faudrait allouer capturé habitants de la ville en "cellules" et la collecte des ordures ou de comptage de référence pour la libération de la mémoire. C++ n'est pas le fait, et malheureusement ce faire C++ lambdas beaucoup moins utiles et plus dangereux que dans d'autres langages comme Lisp, Python ou Javascript.
Plus précisément dans mon expérience, vous devez éviter à tout prix implicite de la capture par référence (par exemple en utilisant le
[&](…){…}
forme) pour lambda objets qui survivent à la portée locale parce que c'est une recette pour une segmentation plus tard lors de l'entretien.Toujours planifier attentivement à ce que la capture et comment et sur la durée de vie de la capture de références.
Bien sûr, il est sûr de capturer tout ce par référence avec
[&]
si tout ce que vous faites est tout simplement à l'aide de la lambda dans le même champ d'application de code d'accès par exemple à des algorithmes tels questd::sort
sans avoir à définir un nommé comparateur de fonction à l'extérieur de la fonction.Une approche qui peut fonctionner parfois capture par valeur un
shared_ptr
à un segment de mémoire alloué par l'état. C'est essentiellement mise en œuvre par la main ce que Python n'automatiquement (mais attention aux cycles de référence pour éviter les fuites de mémoire: Python est un garbage collector, le C++ n'est pas).[&]
est très bien si la durée de vie de la fermeture et de ses copies est limité à l'affichage de{}
, sinon c'est dangereux ou dangereux. En tant que telle durée de vie limitée les fermetures sont en commun, en évitant, en règle générale semble exagéré?Je pensais à des lambdas pour leur utilisation en tant que fermetures, non pas comme un pauvre homme de façon à intégrer le code dans les algorithmes. Je vais le modifier pour l'expliquer.
Meilleure réponse pour pointer vers le funarg problème et pour la comparaison avec d'autres langues.
Je ne pense pas que c'est un bon conseil à-dire que les gens devraient éviter la capture par référence à tout prix. Vous devez le faire quand c'est une bonne idée, ce qui est assez souvent, dans mon expérience. Évidemment, la valeur doit rester dans le champ d'application au cours de la durée de vie de la lambda exécution.
J'ai dit que vous devriez éviter de par défaut capture par référence si le lambda va survivre à la portée parce que c'est une recette pour un désastre. La capture par référence par défaut avec
[&]
est parfaitement OK uniquement pour les lambdas sont immédiatement utilisés.OriginalL'auteur 6502
Quand vous allez hors de portée. Cela permettra de faire une copie de vos locaux.
Lorsque vous êtes dans la portée , de mieux utiliser les références
&
est sûr, alors que dans le second, elle doit être=
.OriginalL'auteur user6787837