2012-07-22 15 views
24

Poniższy kod wypisuje 0, ale spodziewam się zobaczyć 1. Mój wniosek jest taki, że funkcje lambda nie są wywoływane przez faktyczne przekazywanie przechwyconych parametrów do funkcji, co oznacza więcej intuicyjny. Czy mam rację, czy też czegoś brakuje?C++ 11 przechwytywanie lambda według wartości przechwytywane w miejscu deklaracji

#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; 
} 

Odpowiedz

23

funkcje lambda są wywoływane przez faktycznie przechodząc uchwycone parametrów do funkcji.

value jest równe 0 w punkcie, w którym zdefiniowano lambda (i przechwycono value). Ponieważ przechwytujesz według wartości, po zrobieniu nie ma znaczenia, co zrobisz z value.

Jeśli przechwyciłeś value przez odniesienie, zobaczysz 1 wydrukowany, ponieważ mimo że punkt przechwytywania jest nadal taki sam (definicja lambda), drukowałbyś bieżącą wartość przechwyconego obiektu, a nie kopię z tego stworzone, gdy zostało przechwycone.

+0

dzięki. Myślałem o wartości-wartości, ponieważ aktualizacje wartości nie będą widoczne poza funkcją. Wydaje się jednak, że oznacza to również, że aktualizacje poza funkcją nie będą widoczne wewnątrz funkcji. – perreal

+6

To, co może być mylące, brzmi: wartość oznacza "wygeneruj kopię". Lambda ma kopię zmiennej, jej wartość jest podana w momencie deklaracji lambda. Ponieważ lambda ma prywatną kopię, oryginalny obiekt nie jest modyfikowany ani odczytywany wewnątrz lambda. Właśnie dlatego istnieje uchwycenie przez odniesienie w rzeczywistości, aby umożliwić sprawę, którą chcesz zobaczyć/zmodyfikować oryginał. – Klaim

12

Tak, przechwytywania dokonuje się w momencie, w którym zadeklarowano lambda, a nie po jej wywołaniu. Pomyśl o lambdzie jako obiekcie funkcyjnym, którego konstruktor przyjmuje uchwycone zmienne jako parametry i przypisuje je do odpowiednich zmiennych składowych (wartości lub odniesienia, w zależności od trybu przechwytywania). Faktyczne wywołanie lambda nie ma magii, jest po prostu regularne wywołanie operator() podstawowego obiektu funkcji.

Przechwytywanie rzeczy w punkcie wywołania nie ma większego sensu - co zostanie przechwycone, jeśli lambda została zwrócona lub przekazana jako parametr innej funkcji i wywołana tam? W rzeczywistości istnieją języki, które zachowują się w ten sposób - jeśli odwołasz się do zmiennej x w funkcji, przyjmuje się, że odwołuje się ona do dowolnej zmiennej o nazwie x, aktualnie znajdującej się w zasięgu w punkcie połączenia. Nazywa się to dynamicznym określaniem zakresu. Alternatywą, używaną przez większość języków, ponieważ sprawia ona, że ​​rozumowanie o programach jest o wiele prostsze, nazywa się zakresy leksykalne.

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

5

Problemem jest to, że funkcja drukowania jest przechwytywanie przez wartość, a nie przez odniesienie.

#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; 
} 

Wyjścia 0 i 1 zgodnie z oczekiwaniami. Pierwsza jest przechwytywana przez wartość i drukuje wartość w punkcie przechwytywania; drugi przechwytuje referencję, a następnie drukuje jej wartość.

Powiązane problemy