C ++

Лямбда-выражения в C ++

Лямбда-выражения в C ++

Почему лямбда-выражение?

Рассмотрим следующее утверждение:

    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<возврат 0;

Результат:

для основной функции
Сейчас
для функции обратного вызова

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

Конечный возвращаемый тип

Тип возврата лямбда-выражения - auto, что означает, что компилятор определяет тип возвращаемого значения из возвращаемого выражения (при его наличии). Если программист действительно хочет указать тип возвращаемого значения, он сделает это, как в следующей программе:

#включать
используя пространство имен std;
auto fn = [] (int param) -> int

int answer = param + 3;
ответный ответ;
;
int main ()

auto variab = fn (2);
cout << variab << '\n';
возврат 0;

Выход 5. После списка параметров печатается стрелка оператора. За ним следует возвращаемый тип (в данном случае int).

Закрытие

Рассмотрим следующий фрагмент кода:

struct Cla

int id = 5;
char ch = 'а';
obj1, obj2;

Здесь Cla - это имя класса структуры.  Obj1 и obj2 - это два объекта, которые будут созданы из класса структуры. Лямбда-выражение похоже по реализации. Определение лямбда-функции - это своего рода класс. Когда лямбда-функция вызывается (вызывается), объект создается из ее определения. Этот объект называется закрытием. Это замыкание, которое выполняет ту работу, которую должна выполнять лямбда.

Однако при кодировании лямбда-выражения, подобного приведенной выше структуре, obj1 и obj2 будут заменены аргументами соответствующих параметров. Следующая программа иллюстрирует это:

#включать
используя пространство имен std;
auto fn = [] (int param1, int param2)

int answer = param1 + param2;
ответный ответ;
(2, 3);
int main ()

auto var = fn;
cout << var << '\n';
возврат 0;

Выход 5. Аргументы - 2 и 3 в скобках. Обратите внимание, что вызов функции лямбда-выражения, fn, не принимает никаких аргументов, поскольку аргументы уже были закодированы в конце определения лямбда-функции.

Заключение

Лямбда-выражение - это анонимная функция. Он состоит из двух частей: класса и объекта. Его определение - это своего рода класс. При вызове выражения объект формируется из определения. Этот объект называется закрытием. Это замыкание, которое выполняет ту работу, которую должна выполнять лямбда.

Чтобы лямбда-выражение получало переменную из внешней области функции, ему необходимо непустое предложение захвата в теле функции.

5 лучших эргономичных компьютерных мышей для Linux
Вызывает ли длительное использование компьютера боль в запястье или пальцах?? Вы страдаете от скованности суставов и постоянно должны пожимать руки? В...
Как изменить настройки мыши и сенсорной панели с помощью Xinput в Linux
Большинство дистрибутивов Linux по умолчанию поставляются с библиотекой libinput для обработки событий ввода в системе. Он может обрабатывать события ...
Переназначьте кнопки мыши по-разному для разных программ с помощью X-Mouse Button Control
Может быть, вам нужен инструмент, который мог бы изменять управление вашей мышью с каждым приложением, которое вы используете. В этом случае вы можете...