Je comprends que la bonne façon de capturer this
(pour modifier les propriétés d'un objet) dans un lambda est la suivante:
auto f = [this] () { /* ... */ };
Mais je suis curieux de connaître la particularité suivante que j'ai vue:
class C {
public:
void foo() {
// auto f = [] () { // this not captured
auto f = [&] () { // why does this work?
// auto f = [&this] () { // Expected ',' before 'this'
// auto f = [this] () { // works as expected
x = 5;
};
f();
}
private:
int x;
};
La bizarrerie par laquelle je suis confus (et j'aimerais avoir une réponse) est la raison pour laquelle ce qui suit fonctionne:
auto f = [&] () { /* ... */ }; // capture everything by reference
Et pourquoi je ne peux pas capturer explicitement this
par référence:
auto f = [&this] () { /* ... */ }; // a compiler error as seen above.
this
ne peut pas être modifiée, elle n'est pas assez grande pour faire une référence plus rapidement ... et de toute façon , elle n'existe pas , donc elle a pas de vie réelle, ce qui signifie que toute référence à celle-ci serait suspendue par définition.this
est une prvalue, pas une lvalue.Réponses:
La raison
[&this]
ne fonctionne pas, c'est qu'il s'agit d'une erreur de syntaxe. Chaque paramètre séparé par des virgules dans lelambda-introducer
est uncapture
:capture: identifier & identifier this
Vous pouvez voir que la
&this
syntaxe n'est pas autorisée. La raison pour laquelle il n'est pas autorisé est que vous ne voudriez jamais capturerthis
par référence, car il s'agit d'un petit pointeur const. Vous ne voudrez jamais le transmettre que par valeur - donc le langage ne prend tout simplement pas en charge la capturethis
par référence.Pour capturer
this
explicitement, vous pouvez utiliser[this]
commelambda-introducer
.Le premier
capture
peut être uncapture-default
qui est:capture-default: & =
Cela signifie capturer automatiquement tout ce que j'utilise, par référence (
&
) ou par valeur (=
) respectivement - cependant le traitement dethis
est spécial - dans les deux cas, il est capturé par valeur pour les raisons données précédemment (même avec une capture par défaut de&
, ce qui signifie généralement capture par référence).5.1.2.7/8:
Ainsi, le lambda agit comme s'il faisait partie de la fonction membre englobante lors de l'utilisation de noms de membres (comme dans votre exemple l'utilisation du nom
x
), donc il générera des «utilisations implicites» de lathis
même manière qu'une fonction membre.Vous pouvez donc utiliser
[this]
,[&]
,[=]
ou[&,this]
commelambda-introducer
pour capturer lethis
pointeur en valeur.Cependant
[&this]
et[=, this]
sont mal formés. Dans le dernier cas, gcc avertit avec indulgence pour[=,this]
celaexplicit by-copy capture of ‘this’ redundant with by-copy capture default
plutôt que pour des erreurs.la source
[&]
si vous faites quelque chose comme créer un bloc à passer dans une structure de contrôle", mais capturer explicitement si vous produisez un lambda qui sera utilisé à des fins moins simples.[&]
est une idée horrible si le lambda va survivre à la portée actuelle. Cependant, de nombreuses utilisations des lambdas ne sont que des moyens de passer des blocs aux structures de contrôle, et le bloc ne survivra pas au bloc dans lequel il a été créé.this
c'est un mot-clé, cethis
n'est pas un identifiant.Parce que la norme n'a pas
&this
dans les listes de captures:N4713 8.4.5.2 Captures:
lambda-capture: capture-default capture-list capture-default, capture-list capture-default: & = capture-list: capture...opt capture-list, capture...opt capture: simple-capture init-capture simple-capture: identifier &identifier this * this init-capture: identifier initializer &identifier initializer
Ainsi, la norme garantit
this
et*this
est valide, et&this
est invalide. De plus, capturerthis
signifie capturer*this
(qui est une lvalue, l'objet lui-même) par référence , plutôt que capturer lethis
pointeur par valeur !la source
*this
capture l'objet par valeur