return [=](int j){return offset+j;};
}
При каждом вызове make_offseter
с помощью обертки std::function<>
создается новый содержащий лямбда-функцию объект. Возвращенная функция добавляет указанное смещение к любому переданному ей параметру. Например, следующая программа
int main() {
std::function offset_42 = make_offseter(42);
std::function offset_123 = make_offseter(123);
std::cout <<
offset_42(12) << "," << offset_123(12) << std::endl;
std::cout <<
offset_42(12) << "," << offset_123(12) << std::endl;
}
два раза выведет числа 54, 135
, потому что функция, возвращенная после первого обращения к make_offseter
, всегда добавляет 42 к переданному ей аргументу Напротив, функция, возвращенная после второго обращения к make_offseter
, добавляет к своему аргументу 123. Это самый безопасный вид захвата локальных переменных — все значения копируются, поэтому лямбда-функцию можно вернуть и вызывать вне контекста функции, в которой она была создана. Но это не единственно возможное решение, можно захватывать локальные переменные и по ссылке. В таком случае попытка вызвать лямбда-функцию после того, как переменные, на которые указывают ссылки, были уничтожены в результате выхода из области видимости объемлющей их функции или блока, приведёт к неопределённому поведению, точно так же, как обращение к уничтоженной переменной в любом другом случае.
Лямбда-функция, захватывающая все локальные переменные по ссылке, начинается интродуктором [&]
:
int main() {
int offset = 42; ←
(1)
std::function offset_a =
[&](int j){return offset + j;};←
(2)
offset = 123; ←
(3)
std::function offset_b =
[&](int j){return offset + j;};←
(4)
std::cout <<
offset_a(12) << "," << offset_b(12) << std::endl; ←
(5)
offset = 99; ←
(6)
std::cout <<
offset_a(12) << "," << offset_b(12) << std::endl; ←
(7)
}
Если функция make_offseter
из предыдущего примера захватывала копию смещения offset
, то функция offset_a
в этом примере, начинающаяся интродуктором [&]
, захватывает offset
по ссылке (2). Неважно, что начальное значение offset
было равно 42 (1); результат вызова offset_a(12)
зависит от текущего значения offset
. Значение offset
было изменено на 123 (3)перед порождением второй (идентичной) лямбда-функции offset_b
(4), но эта вторая функция снова производит захват по ссылке, поэтому результат, как и прежде, зависит от текущего значения offset
.
Теперь при печати первой строки (5), offset
всё еще равно 123, поэтому печатаются числа 133, 135
. Однако к моменту печати второй строки (7) offset
стало равно 99 (6), поэтому печатается 111, 111
. И offset_a
, и offset_b
прибавляют текущее значение offset
(99) к переданному аргументу (12).
Но ведь это С++, поэтому вам не обязательно выбирать между всем или ничем; вполне можно захватывать одни переменные по значению, а другие по ссылке. Более того, можно даже указывать, какие именно переменные захватить. Нужно лишь изменить лямбда-интродуктор. Если требуется скопировать все видимые переменные, кроме одной-двух, то воспользуйтесь интродуктором [=]
, но после знака равенства перечислите переменные, захватываемые по ссылке, предпослав им знаки амперсанда. В следующем примере печатается 1239
, потому что переменная i
копируется в лямбда-функцию, a j
и k
захватываются по ссылке:
int main() {
int i=1234, j=5678, k=9;
std::function f=[=,&j,&k] {return i+j+k;};
i = 1;
j = 2;
k = 3;
std::cout << f() << std::endl;
}
Можно поступить и наоборот — по умолчанию захватывать по ссылке, но некоторое подмножество переменных копировать. В таком случае воспользуйтесь интродуктором [&]
, а после знака амперсанда перечислите переменные, захватываемые по значению. В следующем примере печатается 5688
, потому что i
захватывается по ссылке, a j
и k
копируются:
int main() {
int i=1234, j=5678, k= 9;
std::function f=[&,j,k] {return i+j+k;};
i = 1;
j = 2;
k = 3;
std::cout << f() << std::endl;
}
Если требуется захватить только именованные переменные, то можно опустить знак =
или &
и просто перечислить захватываемые переменные, предпослав знак амперсанда тем, что должны захватываться по ссылке, а не по значению. В следующем примере печатается 5682
, потому что i
и k
захвачены по ссылке, a j
скопирована
Читать дальше