Почему лямбда-выражение?
Рассмотрим следующее утверждение:
int myInt = 52;Здесь myInt - это идентификатор, lvalue. 52 - буквальное значение, prvalue. Сегодня можно специально закодировать функцию и поставить ее на позицию 52. Такая функция называется лямбда-выражением. Рассмотрим также следующую короткую программу:
#включатьиспользуя пространство имен std;
int fn (int par)
int answer = par + 3;
ответный ответ;
int main ()
fn (5);
возврат 0;
Сегодня можно специально закодировать функцию и поместить ее на позицию аргумента 5 при вызове функции, fn (5). Такая функция называется лямбда-выражением. Лямбда-выражение (функция) в этой позиции является prvalue.
Любой литерал, кроме строкового, является prvalue. Лямбда-выражение - это особый дизайн функции, который можно использовать как литерал в коде. Это анонимная (безымянная) функция. В этой статье объясняется новое первичное выражение C ++, называемое лямбда-выражением. Для понимания этой статьи необходимы базовые знания C ++.
Содержание статьи
- Иллюстрация лямбда-выражения
- Части лямбда-выражения
- Захватывает
- Классическая схема функции обратного вызова с лямбда-выражением
- Конечный возвращаемый тип
- Закрытие
- Заключение
Иллюстрация лямбда-выражения
В следующей программе переменной присваивается функция, которая является лямбда-выражением:
#включатьиспользуя пространство имен std;
auto fn = [] (параметр int)
int answer = param + 3;
ответный ответ;
;
int main ()
auto variab = fn (2);
cout << variab << '\n';
возврат 0;
Результат:
5Вне функции main () есть переменная fn. Тип авто. Авто в этой ситуации означает, что фактический тип, такой как int или float, определяется правым операндом оператора присваивания (=). Справа от оператора присваивания находится лямбда-выражение. Лямбда-выражение - это функция без предыдущего возвращаемого типа. Обратите внимание на использование и расположение квадратных скобок, []. Функция возвращает 5, целое число, которое определит тип для fn.
В функции main () есть инструкция:
auto variab = fn (2);Это означает, что fn за пределами main () становится идентификатором функции. Его неявные параметры - это параметры лямбда-выражения. Тип переменной - авто.
Обратите внимание, что лямбда-выражение заканчивается точкой с запятой, как и определение класса или структуры, заканчивается точкой с запятой.
В следующей программе функция, которая является лямбда-выражением, возвращающим значение 5, является аргументом другой функции:
#включатьиспользуя пространство имен std;
void otherfn (интервал №1, интервал (* ptr) (интервал))
интервал no2 = (* ptr) (2);
cout << no1 << " << no2 << '\n';
int main ()
otherfn (4, [] (параметр int)
int answer = param + 3;
ответный ответ;
);
возврат 0;
Результат:
4 5Здесь есть две функции: лямбда-выражение и функция otherfn (). Лямбда-выражение - это второй аргумент функции otherfn (), вызываемой в main (). Обратите внимание, что лямбда-функция (выражение) не заканчивается точкой с запятой в этом вызове, потому что здесь это аргумент (а не отдельная функция).
Параметр лямбда-функции в определении функции otherfn () является указателем на функцию. Указатель имеет имя ptr. Имя ptr используется в определении otherfn () для вызова лямбда-функции.
Заявление,
интервал no2 = (* ptr) (2);В определении otherfn () он вызывает лямбда-функцию с аргументом 2. Возвращаемое значение вызова "(* ptr) (2)" из лямбда-функции присваивается номеру no2.
Вышеупомянутая программа также показывает, как лямбда-функция может использоваться в схеме функции обратного вызова C ++.
Части лямбда-выражения
Части типичной лямбда-функции следующие:
[] ()- [] - это предложение о захвате. В нем могут быть предметы.
- () для списка параметров.
- для тела функции. Если функция стоит отдельно, она должна заканчиваться точкой с запятой.
Захватывает
Определение лямбда-функции может быть присвоено переменной или использоваться в качестве аргумента для вызова другой функции. Определение для такого вызова функции должно иметь в качестве параметра указатель на функцию, соответствующую определению лямбда-функции.
Определение лямбда-функции отличается от определения нормальной функции. Его можно присвоить переменной в глобальной области видимости; эта функция, назначенная переменной, также может быть закодирована внутри другой функции. При назначении переменной глобальной области видимости ее тело может видеть другие переменные в глобальной области видимости. При назначении переменной внутри обычного определения функции, ее тело может видеть другие переменные в области действия функции только с помощью предложения захвата, [].
Предложение захвата [], также известное как лямбда-вводчик, позволяет отправлять переменные из окружающей (функции) области в тело функции лямбда-выражения. Говорят, что тело функции лямбда-выражения захватывает переменную при получении объекта. Без предложения захвата [] переменная не может быть отправлена из окружающей области в тело функции лямбда-выражения. Следующая программа иллюстрирует это с помощью области видимости функции main () как окружающей области:
#включатьиспользуя пространство имен std;
int main ()
int id = 5;
auto fn = [id] ()
cout << id << '\n';
;
fn ();
возврат 0;
На выходе 5. Без имени id внутри [] лямбда-выражение не увидело бы идентификатор переменной области видимости функции main ().
Захват по ссылке
В приведенном выше примере использования предложения захвата выполняется захват по значению (см. Подробности ниже). При захвате по ссылке местоположение (хранилище) переменной e.грамм., идентификатор, указанный выше, из окружающей области видимости, доступен внутри тела лямбда-функции. Таким образом, изменение значения переменной внутри тела лямбда-функции изменит значение той же переменной в окружающей области. Каждой переменной, повторяющейся в предложении захвата, предшествует амперсанд (&) для достижения этой цели. Следующая программа иллюстрирует это:
#включатьиспользуя пространство имен std;
int main ()
int id = 5; поплавок ft = 2.3; char ch = 'A';
auto fn = [& id, & ft, & ch] ()
id = 6; фут = 3.4; ch = 'B';
;
fn ();
cout << id << ", " << ft << ", " << ch << '\n';
возврат 0;
Результат:
6, 3.4, БПодтверждение того, что имена переменных внутри тела функции лямбда-выражения относятся к тем же переменным вне лямбда-выражения.
Захват по стоимости
При захвате по значению копия местоположения переменной в окружающей области видимости становится доступной внутри тела лямбда-функции. Хотя переменная внутри тела лямбда-функции является копией, на данный момент ее значение не может быть изменено внутри тела. Чтобы добиться захвата по значению, каждой переменной, повторяющейся в предложении захвата, ничего не предшествует. Следующая программа иллюстрирует это:
#включатьиспользуя пространство имен std;
int main ()
int id = 5; поплавок ft = 2.3; char ch = 'A';
auto fn = [id, ft, ch] ()
// id = 6; фут = 3.4; ch = 'B';
cout << id << ", " << ft << ", " << ch << '\n';
;
fn ();
id = 6; фут = 3.4; ch = 'B';
cout << id << ", " << ft << ", " << ch << '\n';
возврат 0;
Результат:
5, 2.3, А6, 3.4, Б
Если убрать индикатор комментария, программа не скомпилируется. Компилятор выдаст сообщение об ошибке, что переменные внутри определения лямбда-выражения в теле функции не могут быть изменены. Хотя переменные не могут быть изменены внутри лямбда-функции, они могут быть изменены вне лямбда-функции, как показано в выходных данных приведенной выше программы.
Смешивание захватов
Захват по ссылке и захват по значению можно смешивать, как показано в следующей программе:
#включатьиспользуя пространство имен std;
int main ()
int id = 5; поплавок ft = 2.3; char ch = 'A'; bool bl = true;
auto fn = [id, ft, & ch, & bl] ()
ch = 'B'; bl = ложь;
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn ();
возврат 0;
Результат:
5, 2.3, Б, 0Когда все захвачено, по ссылке:
Если все переменные, которые должны быть захвачены, захватываются по ссылке, тогда в предложении захвата будет достаточно одного &. Следующая программа иллюстрирует это:
#включатьиспользуя пространство имен std;
int main ()
int id = 5; поплавок ft = 2.3; char ch = 'A'; bool bl = true;
auto fn = [&] ()
id = 6; фут = 3.4; ch = 'B'; bl = ложь;
;
fn ();
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
возврат 0;
Результат:
6, 3.4, Б, 0Если некоторые переменные должны быть захвачены по ссылке, а другие по значению, то один & будет представлять все ссылки, а каждой из остальных не будет ничего предшествовать, как показано в следующей программе:
используя пространство имен std;int main ()
int id = 5; поплавок ft = 2.3; char ch = 'A'; bool bl = true;
auto fn = [&, id, ft] ()
ch = 'B'; bl = ложь;
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn ();
возврат 0;
Результат:
5, 2.3, Б, 0Обратите внимание, что & alone (i.е., & без идентификатора) должен быть первым символом в предложении захвата.
Когда все захвачено, по значению:
Если все переменные, которые должны быть захвачены, должны быть захвачены по значению, тогда в предложении захвата будет достаточно одного =. Следующая программа иллюстрирует это:
#включатьиспользуя пространство имен std;
int main ()
int id = 5; поплавок ft = 2.3; char ch = 'A'; bool bl = true;
auto fn = [=] ()
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn ();
возврат 0;
Результат:
5, 2.3, А, 1Примечание: = только для чтения, на данный момент.
Если одни переменные должны быть захвачены по значению, а другие по ссылке, то один = будет представлять все скопированные переменные, доступные только для чтения, а остальные будут иметь &, как показано в следующей программе:
#включатьиспользуя пространство имен std;
int main ()
int id = 5; поплавок ft = 2.3; char ch = 'A'; bool bl = true;
auto fn = [=, & ch, & bl] ()
ch = 'B'; bl = ложь;
cout << id << ", " << ft << ", " << ch << ", " << bl << '\n';
;
fn ();
возврат 0;
Результат:
5, 2.3, Б, 0Обратите внимание, что только = должен быть первым символом в предложении захвата.
Классическая схема функции обратного вызова с лямбда-выражением
Следующая программа показывает, как можно реализовать классическую схему функции обратного вызова с помощью лямбда-выражения:
#включатьиспользуя пространство имен std;
char * output;
auto cba = [] (char out [])
output = out;
;
void PrincipalFunc (char input [], void (* pt) (char []))
(* pt) (ввод);
cout<<"for principal function"<<'\n';
void fn ()
cout<<"Now"<<'\n';
int main ()
char input [] = "для функции обратного вызова";
PrincipalFunc (ввод, cba);
fn ();
cout<