101 votes

Pourquoi ne puis-je pas capturer ce by-reference ('&this') en lambda ?

Je comprends la façon correcte de capturer this (pour modifier les propriétés de l'objet) dans une 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 qui m'échappe (et à laquelle j'aimerais qu'on réponde) est la suivante : pourquoi cela fonctionne-t-il ?

auto f = [&] () { /* ... */ }; // capture everything by reference

Et pourquoi je ne peux pas explicitement capturer this par référence :

auto f = [&this] () { /* ... */ }; // a compiler error as seen above.

123voto

Andrew Tomazos Points 18711

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 le lambda-introducer es un capture :

capture:
    identifier
    & identifier
    this

Vous pouvez voir que &this n'est pas autorisé syntaxiquement. La raison pour laquelle ce n'est pas autorisé est que vous ne voudriez jamais capturer this par référence, car il s'agit d'un petit pointeur constant. Vous ne voudriez jamais le passer que par valeur - le langage ne supporte donc pas la capture this par référence.

Pour capturer this explicitement, vous pouvez utiliser [this] comme le 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 - toutefois, le traitement des this est spéciale - dans les deux cas elle est capturée 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 la capture par référence).

5.1.2.7/8 :

Pour les besoins de la recherche de noms (3.4), la détermination du type et de la valeur de l'élément this (9.3.2) et en transformant les expressions id- se référant à des membres non statiques de la classe en expressions d'accès aux membres de la classe à l'aide de la méthode (*this) (9.3.1), l'énoncé composé [DE LA LAMBDA] est considéré dans le contexte de l'expression lambda.

Ainsi, le lambda agit comme s'il faisait partie de la fonction membre englobante lorsqu'il utilise les noms des membres (comme dans votre exemple, l'utilisation du nom x ), il générera donc des "utilisations implicites" de this comme le fait une fonction membre.

Si une capture lambda inclut une capture par défaut qui est & les identificateurs dans la capture lambda ne doivent pas être précédés de & . Si une capture lambda inclut une capture-défaut qui est = la capture lambda ne doit pas contenir de this et chaque identifiant qu'il contient doit être précédé du signe & . Un identifiant ou this ne doit pas apparaître plus de une fois dans une capture lambda.

Vous pouvez donc utiliser [this] , [&] , [=] o [&,this] en tant que lambda-introducer pour capturer le this par la valeur du pointeur.

Cependant [&this] y [=, this] sont mal formées. Dans le dernier cas, gcc prévient de manière indulgente de [=,this] que explicit by-copy capture of ‘this’ redundant with by-copy capture default plutôt que des erreurs.

7voto

陳 力 Points 1690

Parce que le standard 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
  1. Pour les besoins de la capture lambda, une expression fait potentiellement référence à des entités locales comme suit :

    7.3 Une expression this fait potentiellement référence à *this.

Ainsi, les garanties standard this y *this est valide, et &this est invalide. De même, la capture this moyens de capturer *this (qui est une lvalue, l'objet lui-même) par référence , plutôt que capturer this pointeur par valeur !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X