Je comprends que la manière correcte 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 savoir 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 à laquelle je suis confus (et que j'aimerais obtenir une réponse) explique pourquoi les œuvres suivantes:
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.
La raison pour laquelle [&this]
ne fonctionne pas est qu’il s’agit d’une erreur de syntaxe. Chaque paramètre séparé par une virgule dans le lambda-introducer
est un capture
:
capture:
identifier
& identifier
this
Vous pouvez voir que &this
n'est pas autorisé syntaxiquement. La raison pour laquelle cela n'est pas autorisé est que vous ne voudriez jamais capturer this
par référence, car il s'agit d'un petit pointeur const. Vous ne voudrez jamais le transmettre que par valeur - de sorte que le langage ne prend tout simplement pas en charge la capture de this
par référence.
Pour capturer this
explicitement, vous pouvez utiliser [this]
en tant que lambda-introducer
.
Le premier capture
peut être un capture-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 de this
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 une capture par référence).
5.1.2.7/8:
Aux fins de recherche de nom (3.4), déterminer le type et la valeur de
this
(9.3.2) et transformer les expressions id faisant référence à des membres de classe non statiques en expressions d'accès de membre de classe à l'aide de(*this)
( 9.3.1), l'instruction composée [OF THE LAMBDA] est considérée dans le contexte de l'expression lambda.
Ainsi, le lambda agit comme s'il faisait partie de la fonction de membre englobant lors de l'utilisation de noms de membres (comme dans votre exemple, l'utilisation du nom x
), de sorte qu'il générera des "utilisations implicites" de this
comme le fait une fonction membre.
Si une capture lambda comprend une valeur par défaut de capture qui est
&
, les identifiants de la capture lambda ne doivent pas être précédés de&
. Si une capture lambda comprend une valeur par défaut de capture qui est=
, la capture lambda ne doit pas contenirthis
et chaque identifiant qu'elle contient doit être précédé de&
. Un identifiant outhis
ne doit pas apparaître plus d'une fois dans une capture lambda.
Vous pouvez donc utiliser [this]
, [&]
, [=]
ou [&,this]
en tant que lambda-introducer
pour capturer le pointeur this
par valeur.
Toutefois, [&this]
et [=, this]
sont mal formés. Dans le dernier cas, gcc avertit pardon pour [=,this]
que explicit by-copy capture of ‘this’ redundant with by-copy capture default
plutôt que par des erreurs.
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
Pour les besoins de la capture lambda, une expression fait potentiellement référence à des entités locales comme suit:
7.3 A cette expression fait potentiellement référence à * this.
Ainsi, les garanties standard this
et *this
est valide et &this
est invalide. Aussi, capturer this
signifie capturer *this
_ (qui est une valeur, l'objet lui-même) par référence, plutôt que capture this
pointeur par valeur!