2016-06-05 12 views
12

sto leggendo attraverso il C++ Sezione lambda nel capitolo 3 del this book e il seguente codice mi confonde:C++ lambda cattura per valore

int x = 0; 
int y = 42; 
auto qqq = [x, &y] { 
    std::cout << "x: " << x << std::endl; 
    std::cout << "y: " << y << std::endl; 
    ++y; 
}; 
x = y = 77; 
qqq(); 
qqq(); 
std::cout << "final y: " << y << std::endl; 

Questo codice stampa fuori:

x: 0 
y: 77 
x: 0 
y: 78 
final y: 79 

Perché qqq() non registri che x è cambiato in 77? È stato affermato che passare per valore significa che possiamo leggere ma non modificare i dati leggibili dove è stato definito il lambda. Significa che non possiamo vedere i cambiamenti dopo la sua definizione?

+0

Hai ragione, quel libro non fornisce una spiegazione molto buona. Le risposte qui sotto fanno un lavoro migliore. – jdigital

risposta

14

Questo perché la variabile viene catturata dal valore (cioè copiata) solo una volta, quando si definisce la lambda. Non è "aggiornato" come potresti credere. Il codice è approssimativamente equivalente a:

#include <iostream> 

int x = 0; 
struct Lambda 
{ 
    int _internal_x; // this is used to "capture" x ONLY ONCE 
    Lambda(): _internal_x(x) {} // we "capture" it at construction, no updates after 
    void operator()() const 
    { 
     std::cout << _internal_x << std::endl; 
    } 
} qqq; 

int main() 
{ 
    qqq(); 
    x = 77; // this has no effect on the internal state of the lambda 
    qqq(); 
} 

Live on Coliru

9

Legandosi una variabile in base al valore di una chiusura lambda, si sta efficacemente copiando il valore della variabile in una variabile indipendente che si trova all'interno dell'oggetto lambda. Allo stesso modo, legando una variabile per riferimento, si sta rendendo tale variabile interna come un riferimento alla variabile originale, quindi in grado di "vedere le modifiche" sulla variabile originale.

Guardalo così. Il seguente codice ...

#include <iostream> 

int main() 
{ 
    int x = 0; 
    int y = 42; 
    auto qqq = [x, &y] { 
     std::cout << "x: " << x << std::endl; 
     std::cout << "y: " << y << std::endl; 
     ++y; 
    }; 
    x = y = 77; 
    qqq(); 
    qqq(); 
    std::cout << "final y: " << y << std::endl; 
} 

è (un po ', ma non esattamente) zucchero sintattico per ...

#include <iostream> 

class MyLambda 
{ 
    private: 
     int x; 
     int& y; 

    public: 
     MyLambda(int x, int& y) : x(x), y(y) {} 

     void operator()() 
     { 
      std::cout << "x: " << x << std::endl; 
      std::cout << "y: " << y << std::endl; 
      ++y; 
     } 
}; 

int main() 
{ 
    int x = 0; 
    int y = 42; 
    MyLambda qqq = MyLambda(x, y); 
    x = y = 77; 
    qqq(); 
    qqq(); 
    std::cout << "final y: " << y << std::endl; 
} 

Con l'eccezione della speciale sintassi lambda e il fatto che si puo' t riferimento direttamente al tipo di lambda. Confrontando il primo con il codice successivo, dovresti essere in grado di chiarire la tua comprensione delle chiusure.

Problemi correlati