2016-06-05 3 views
12

Я читаю через C++ лямбда раздел в главе 3 this book и следующий код путает меня:C++ лямбда захват значения

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; 

Этот код выводит:

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

Почему QQQ() не регистрируется, что x изменился на 77? Было указано, что передача по значению означает, что мы можем читать, но не изменять данные, доступные для чтения, где была определена лямбда. Означает ли это, что мы не можем видеть изменения после определения?

+0

Вы правы, эта книга не дает очень хорошего объяснения. Ниже приведены ответы на эту работу. – jdigital

ответ

14

Это потому, что переменная захватывается значением (т. Е. Скопировано) только один раз, когда вы определяете лямбда. Он не «обновляется», как вы можете поверить. Код примерно эквивалентно:

#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

4

лямбда использует значение в момент лямбда определяется (это делает копию), а не в момент ее вызова.

Это может помочь: How are Lambda Closures Implemented?

9

Путем связывания переменной по значению закрытия лямбда, вы эффективно копировать значение переменной в отдельную переменную, которая находится внутри объекта лямбда. Аналогично, связывая переменную по ссылке, вы делаете такую ​​внутреннюю переменную вместо ссылки на исходную переменную, тем самым имея возможность «видеть изменения» в исходной переменной.

Посмотрите на это вот так. Следующий код ...

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

Is (вид, но не точно) синтаксически ...

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

За исключением специального синтаксиса лямбда, и тот факт, что вы можете» t напрямую ссылайтесь на тип лямбды. Сравнивая первое с более поздним кодом, вы должны уметь понять свое понимание закрытия.