web-dev-qa-db-fra.com

Pourquoi ne puis-je pas capturer cette référence par défaut ('& ceci') dans lambda?

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.
72
Anthony Sottile

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 contenir this et chaque identifiant qu'elle contient doit être précédé de &. Un identifiant ou this 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.

99
Andrew Tomazos

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
  1. 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!

4
陳 力