2017-10-09 13 views
2

Dans l'exemple suivant, quelle est la différence entre l'utilisation de [this] dans la liste de capture et l'utilisation de la capture par référence [&] dans la liste de capture, comme indiqué? J'ai essayé les deux et ils produisent la même sortie.Est-ce que [this] et [&] dans un équivalent de liste de capture lambda, lorsqu'il est utilisé dans une classe?

#include <iostream>               

class Test {                 
public:                  
    int x = 2;                 
    void test1(void) { std::cout << "test1" << std::endl; };     

    void test_lambda(void) {             
     auto lambda = [&]() {            
      std::cout << "x: " << x << " y: " << y << " z: " << z << std::endl; 
      this->test1();               
     };                 

     lambda();                
    }                   

protected:                 
    int y = 3;                 

private:                  
    int z = 4;                 
};                   

int main() {                 
    Test t;                 
    t.test_lambda();               
} 

Dans le langage C++ de programmation, Stroustrop dit:

Members are always captured by reference. That is, [this] implies that members are accessed through this rather than copied into the lambda. 

Cela semble impliquer qu'ils peuvent faire la même chose. Si c'est le cas, pourquoi avons-nous besoin de cela?

+0

& peut être utilisé pour la variable spécifique –

+2

Utilisez toujours la capture la moins puissante que vous pouvez utiliser. Non seulement cela rend le raisonnement local plus facile, mais vous pouvez également éviter une erreur de temps en temps. – KABoissonneault

Répondre

2

Selon cppreference:

[&] capture toutes les variables automatiques utilisées dans le corps du lambda par référence et objet courant par référence si elle existe

alors qu'en utilisant [ce] capturera seul le pointeur this.

Ce sera différent lorsque vous avez des variables automatiques dans le champ d'application, par exemple:

struct Test { 
    void run() { 
    int y = 2; 

    // all automatic variables are accessible, both local and members 
    auto l1 = [&](){ cout << x << " " << y << endl; }; 
    l1(); 

    // y is not accessible, x is only because it's a member 
    auto l2 = [this]() { cout << this->x << endl; }; 
    l2(); 
    } 

    int x = 1; 
}; 

int main() { 
    Test t; 
    t.run(); 
} 

Alors, pourquoi avons-nous besoin?

Permettre la capture de [ceci] revient à autoriser la capture de tout autre pointeur. Pourquoi ne pas capturer toutes les variables automatiques tout le temps? Il y a deux raisons, notamment:

  • Encapsulation: Parfois, vous ne voulez pas le lambda de se familiariser avec d'autres valeurs
  • Flexibilité: Parfois, nous devons copier certaines valeurs et passer certains d'entre eux par référence

note: la capture toutes les variables automatiques à l'aide & n'introduit pas les frais de performance supplémentaires, étant donné que le compilateur ne passe t Les variables que nous utilisons à l'intérieur du lambda.

+0

Re: "performance", la citation de cppreference implique que seules les variables utilisées sont capturées . Cela n'a aucun effet sur les performances, car si vous capturez moins que cela, la compilation échouera. –

+0

@DanielH, merci pour l'indice, j'ai raté cela –

1

Le & capture toutes les variables incluses dans la portée par référence. Considérez ceci:

void test_lambda(void) {             
    int dummy = 42;     // capture dummy ?!? 
    auto lambda = [&]() {    // yes         
     std::cout << "x: " << x << " y: " << y << " z: " << z << std::endl; 
     this->test1(); 
     std::cout << dummy;   // ok here              
    };                 
    lambda();                
} 

[this] ne saisira pas les variables locales comme volonté [&].

+0

La question est "Puisque' [&] 'est plus puissant que '[ceci]', pourquoi avons-nous besoin de' [ceci] ' du tout?" –

+1

@RaymondChen parce que vous ne voulez pas capturer des choses que vous ne voulez pas capturer – user463035818

+0

@RaymondChen Étant donné que ce n'est pas dans la question, et la question n'a pas été modifiée, où obtenez-vous cette citation? Je l'ai lu de la même façon quebi303, en demandant si elles sont équivalentes et sinon comment elles diffèrent. –

1

La seule façon de capturer des membres est en capturant this:

  • explicitement [this](){ member = 42; }
  • implicitement par la capture par la valeur [=](){ member = 42;}
  • implicitement par la capture par référence [&](){ member = 42; }

Après sont illégales

  • [&member](){ member = 42; } // illegal
  • [member](){ std::cout << member; } // illegal.

en fonction ensuite de votre intention d'exprimer la restriction de la capture, vous pouvez choisir entre être explicite ou non, en évitant reference capture (pour éviter toute référence possible ballants) ...

+0

Je n'ai pas vérifié la norme donc peut-être c'est une extension de compilateur ou quelque chose, mais empiriquement '[& member = member]() {/*...*/ } 'fonctionne très bien. –

+0

@DanielH: Ceci déclare un * "membre lambda nommé" * initialisé par une variable. Je ne dirai pas que c'est une capture du membre (de la même manière, pour 'auto & ref = member; [& ref]() {/ * ...* /} '), pour moi, c'est juste une initialisation. – Jarod42

+0

Il est appelé une capture de toute façon, et dans ce cas, il s'agit d'une référence à la même variable. Je ne suis pas sûr de la distinction que vous faites entre une capture et une initialisation d'une référence; Y a-t-il un cas où les deux se comportent différemment? –

0

En utilisant [this] capturera seulement this, tandis que [&] capture thiset toutes les variables locales de la portée englobante.

struct example { 
    int foo; 

    example() { 
    int bar; 

    [this]() { 
     foo = {}; 
     bar = {}; // error: 'bar' is not captured 
    }(); 

    [&]() { 
     foo = {}; 
     bar = {}; 
    }(); 
    } 
}; 

Vous pouvez utiliser [this] si votre fermeture sera invoquée en dehors de la portée englobante, comme une fonction membre, comme toutes les variables locales auront été détruites.

struct example { 
    int value = 42; 

    example() { 
    frobnicate = [this]() { 
     std::cout << value << std::endl; 
    }; 
    } 

    std::function<void()> frobnicate; 
}; 

example x; 
x.frobnicate(); 

Vous pouvez utiliser [&] si votre fermeture sera appelée à l'intérieur du champ englobante, comme un morceau de code local, comme les variables locales seront toujours en vie.

struct example { 
    int value = 42; 

    example() { 
    int values[] = {1, 2, 3}; 

    std::for_each(std::begin(values), std::end(values), [&](int& v) { 
     std::cout << v * value << std::endl; 
    }); 
    } 
};