Подтвердить что ты не робот

Попытка понять лямбда

Попытка понять lambdas на С++, я не понимаю, что это:

int multiplier = 5;
auto timesFive = [multiplier](int a) { return a * multiplier; }; 
std::cout << timesFive(2) << '\n'; // Prints 10

multiplier = 15;
std::cout << timesFive(2) << '\n'; // Still prints 2*5 == 10 (???) - Should it be 30?

Когда программа вызывает timesFive() второй раз, я ожидаю, что результат будет 30. Но почему результат Still prints 2*5 == 10, а не prints 2*15 == 30? Возможно, функция лямбда каким-то образом не может отслеживать значение multiplier, даже если мы уже пытались ее захватить?

И каков способ получить желаемый результат?

4b9b3361

Ответ 1

Вы захватили multiplier по значению, что означает, что оно было скопировано в лямбда. Вам нужно записать его по ссылке:

int multiplier = 5;
auto timesFive = [&multiplier](int a) { return a * multiplier; }; 
std::cout << timesFive(2);

multiplier = 15;
std::cout << timesFive(2); 

Ответ 2

Lambdas - это синтаксический сахар для неуязвимого класса и его экземпляра. Иногда вы расширяете свой код до того, что этот непобедимый класс может помочь понять, что происходит.

[ capture_list ]( arg_list ) -> return_value_clause_opt { body };

становится очень грубо (псевдокод):

struct anonymous_type {
  capture_list;
  auto operator()( arg_list ) const -> return_value_clause_opt {
    body
  }
  anonymous_type( capture_list_in ):capture_list(capture_list_in) {}
};

Если вы перечислите переменную в capture_list своим простым именем, она будет скопирована в копию в анонимном классе.

Итак, ваш timesFive стал

struct __secret_name__ {
  int multiplier;
  int operator()(int a) const { return a*multiplier; }
};
int multiplier = 5;
auto timesFive = __secret_name__{multiplier};

Должно быть довольно ясно, что изменение multiplier в приведенном выше коде не изменит поведение timesFive.

Если вы помещаете & перед именем, в анонимном классе помещается ссылка const.

struct __secret_name__ {
  int& multiplier;
  int operator()(int a) const { return a*multiplier; }
};
int multiplier = 5;
auto timesFive = __secret_name__{multiplier};

теперь изменение multiplier изменит поведение timesFive, потому что timesFive содержит ссылку на множитель, а не его копию.


Некоторые подробности пропущены для краткости. Имя __secret_name__ предназначено только для изложения. Переменные-члены lamba на самом деле не являются общедоступными. Лямбда, являющаяся тривиально конструктивной, определяется реализацией, даже если ее данные. Etc.