2012-07-22 7 views
24

Der folgende Code druckt 0, aber ich erwarte eine 1. Meine Schlussfolgerung ist, dass Lambda-Funktionen nicht aufgerufen werden, tatsächlich erfasste Parameter an die Funktionen übergeben, die mehr ist intuitiv. Habe ich Recht oder verpasse ich etwas?C++ 11 Lambda-Capture durch Wert erfasst am Deklarationspunkt

#include <iostream> 
int main(int argc, char **argv){ 
    int value = 0; 
    auto incr_value = [&value]() { value++; }; 
    auto print_value = [ value]() { std::cout << value << std::endl; }; 
    incr_value(); 
    print_value(); 
    return 0; 
} 

Antwort

23

Lambda-Funktionen werden durch Passieren tatsächlich erfassten Parameter an die Funktion aufgerufen wird.

value ist gleich 0 an dem Punkt, an dem das Lambda definiert ist (und value wird erfasst). Da Sie nach Wert erfassen, spielt es keine Rolle, was Sie nach der Erfassung an value tun.

Wenn Sie value per Referenz erfasst hätten, würden Sie eine 1 gedruckt sehen, denn obwohl der Punkt der Erfassung immer noch derselbe ist (die Lambda-Definition), würden Sie den aktuellen Wert des erfassten Objekts und keine Kopie drucken davon erstellt, als es gefangen wurde.

+0

danke. Ich dachte by-Wert, da in Updates auf Wert außerhalb der Funktion nicht sichtbar ist. Es klingt jedoch so, als ob Updates außerhalb der Funktion in der Funktion nicht sichtbar sind. – perreal

+6

Was hier irreführend sein könnte, ist der Wortlaut: Wert bedeutet "eine Kopie erzeugen". Das Lambda hat eine Kopie der Variablen, deren Wert zum Zeitpunkt der Deklaration des Lambda genommen wird. Da das Lambda eine private Kopie hat, wird das ursprüngliche Objekt nicht innerhalb des Lambda geändert oder gelesen. Deshalb gibt es tatsächlich eine Erfassung durch Referenz, um den Fall zu ermöglichen, dass Sie das Original sehen/ändern möchten. – Klaim

12

Ja, die Erfassungen erfolgen an dem Punkt, an dem das Lambda deklariert wird, nicht wenn es aufgerufen wird. Stellen Sie sich ein Lambda als ein Funktionsobjekt vor, dessen Konstruktor die erfassten Variablen als Parameter akzeptiert und sie den entsprechenden Mitgliedsvariablen zuweist (entweder Werte oder Referenzen, abhängig vom Aufnahmemodus). Der eigentliche Aufruf des Lambda hat keine Magie, es ist nur ein regulärer operator() Aufruf des zugrunde liegenden Funktionsobjekts.

Das Erfassen von Dingen am Call Point würde nicht viel Sinn ergeben - was würde gefangen werden, wenn das Lambda zurückgegeben oder als Parameter an eine andere Funktion übergeben und dort aufgerufen würde? Es gibt tatsächlich Sprachen, die sich so verhalten - wenn Sie in einer Funktion auf eine Variable x verweisen, wird angenommen, dass sie sich auf eine beliebige Variable namens x bezieht, die sich zum Zeitpunkt des Aufrufs im Geltungsbereich befindet. Dies wird dynamisches Scoping genannt. Die Alternative, die von den meisten Sprachen verwendet wird, weil sie die Argumentation über Programme vereinfacht, wird als lexikalisches Scoping bezeichnet.

http://en.wikipedia.org/wiki/Lexical_scoping#Lexical_scoping_and_dynamic_scoping

5

Das Problem ist, dass Ihre Druckfunktion von Wert zu erfassen und nicht Bezug genommen wird.

#include <iostream> 
int main(int argc, char **argv){ 
    int value = 0; 
    auto incr_value = [&value]() { value++; }; 
    auto print_value = [ value]() { std::cout << value << std::endl; }; 
    auto print_valueref = [ &value]() { std::cout << value << std::endl; }; 

    incr_value(); 
    print_value(); 
    print_valueref(); 
    return 0; 
} 

Ausgänge 0 und 1 wie erwartet. Die erste wird vom Wert erfasst und gibt den Wert an der Stelle aus, an der sie erfasst wurde. die zweite erfasst die Referenz und gibt dann ihren Wert aus.

Verwandte Themen