В этом примере мы напишем небольшую программу, в которой поработаем с лямбда-выражениями, чтобы понять основные принципы взаимодействия с ними.
1. Для работы с лямбда-выражениями не нужна поддержка библиотек, но мы будем выводить сообщения на консоль и использовать строки, поэтому понадобятся соответствующие заголовочные файлы:
#include
#include
2. В данном примере все действие происходит в функции main
. Мы определим два объекта функций, которые не принимают параметры, и вернем целочисленные константы со значениями 1
и 2
. Обратите внимание: выражение return
окружено фигурными скобками {}
, как это делается в обычных функциях, а круглые скобки ()
, указывающие на функцию без параметров, являются []
должны присутствовать:
int main()
{
auto just_one ( [](){ return 1; } );
auto just_two ( [] { return 2; } );
3. Теперь можно вызвать оба объекта функций, просто написав имя переменных, которые в них сохранены, и добавив скобки. В этой строке их не отличить от
std::cout << just_one() << ", " << just_two() << '\n';
4. Забудем о них и определим еще один объект функции, который называется plus
, — он принимает два параметра и возвращает их сумму:
auto plus ( [](auto l, auto r) { return l + r; } );
5. Использовать такой объект довольно просто, в этом плане он похож на любую другую бинарную функцию. Мы указали, что его параметры имеют тип auto, вследствие чего объект будет работать со всеми типами данных, для которых определен оператор +, например со строками.
std::cout << plus(1, 2) << '\n';
std::cout << plus(std::string{"a"}, "b") << '\n';
6. Не нужно сохранять лямбда-выражение в переменной, чтобы использовать его. Мы также можем определить его
std::cout
<< [](auto l, auto r){ return l + r; }(1, 2)
<< '\n';
7. Далее определим замыкание, которое содержит целочисленный счетчик. При каждом вызове значение этого счетчика будет увеличиваться на 1
и возвращать новое значение. Для указания на то, что замыкание содержит внутренний счетчик, разместим в скобках выражение count = 0
— оно указывает, что переменная count
инициализирована целочисленным значением 0
. Чтобы позволить ему изменять собственные переменные, мы используем ключевое слово mutable
, поскольку в противном случае компилятор не разрешит это сделать:
auto counter (
[count = 0] () mutable { return ++count; }
);
8. Теперь вызовем объект функции пять раз и выведем возвращаемые им значения с целью увидеть, что значение счетчика увеличивается:
for (size_t i {0}; i < 5; ++i) {
std::cout << counter() << ", ";
}
std::cout << '\n';
9. Мы также можем взять существующие переменные и захватить их по ссылке вместо того, чтобы создавать копию значения для замыкания. Таким образом, значение переменной станет увеличиваться в замыкании и при этом будет доступно за его пределами. Для этого мы поместим в скобках конструкцию &a
, где символ &
означает, что мы сохраняем
int a {0};
auto incrementer ( [&a] { ++a; } );
10. Если это работает, то можно вызвать данный объект функции несколько раз, а затем пронаблюдать, действительно ли меняется значение переменной a
:
incrementer();
incrementer();
incrementer();
std::cout
<< "Value of 'a' after 3 incrementer() calls: "
<< a << '\n';
11. Последний пример демонстрирует plus
и принимаем только plus
. Другой параметр имеет значение 10
; его мы сохраняем в объекте функции. Таким образом, мы получаем функцию и назовем ее plus_ten
, поскольку она может добавить значение 10
к единственному принимаемому ею параметру.
auto plus_ten ( [=] (int x) {
return plus(10, x);});
std::cout << plus_ten(5) << '\n';
}