Evgenii Legotckoi
Evgenii LegotckoiАқп. 13, 2018, 6:13 Т.Қ.

C++ тілінде Lambda функцияларын пісіру - 1 бөлім

Мой любимый инструмент в C++ - это лямбда функции, хотя как-то мне говорили, что они кажутся страшными. На самом деле они прелестны. Они значительно упрощают написание программ и позволяют делать довольно интересные решения.

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

Возможные варианты синтаксиса лямбда функций

[ capture ] ( params ) mutable exception attribute -> ret { body }
[ capture ] ( params ) -> ret { body }
[ capture ] ( params ) { body }
[ capture ] { body }

Первый вариант является полным, но не запрещается использовать сокращённые вариации записи функций.

  • capture - список  внешних захватываемых объектов, они могут захватываться как по ссылке, так и копированием.
  • params - список параметров, передаваемых в лямбда функции, данная часть будет аналогична записи аргументов для обычных функций.
  • mutable - использование mutable позволяет модифицировать копии объектов, которые были захвачены копированием. В обычном варианте они не будут модифицироваться.
  • exception - обеспечивает спецификацию исключения, то есть лямбда функции также как и обычные функции могут выкидывать исключения.
  • attribute - обеспечивает спецификацию атрибута, таких атрибутов в спецификации C++ определено всего два ([[noreturn]], [[carries_dependency]])
  • params - список параметров, передаваемых в лямбда функцию
  • ret - возвращаемое значение лямбда функции

Что касается возвращаемого значение, то оно может автоматически выводиться из типа объекта, который возвращается оператором return. Если же в лямбда-функции отсутствует оператор return, то возвращаемое значение будет void.

Лямбда функция создаёт безымянный временный объект уникального безымянного non-union, non-aggregate типа, известного как тип замыкания. Благодаря введению оператора auto в современном стандарте C++ можно объявить объект лямбда функции довольно легко, без прописывания объявления функтора ( std::function ) со всеми апраметрами и возвращаемыми значениями, что делает код более простым и читаемым (для опытного программиста, конечно. Безусловно нужно учитывать то, что новичок быстрее заподозрит неладное, если в объявлении лямбды будет фигурировать std::function, но это уже вопрос практики).

Вот пример объявления простой лямбда функции, которая будет возвращать тип void , поскольку отсутствует хотя бы один оператор return .

#include <iostream>

using namespace std;

int main()
{
    auto myLambda = []()
    {
        cout << "Hello World!" << endl;
    };

    myLambda();

    return 0;
}

Соответственно программный код не скомпилируется, если в лямда-функции будет присутствовать два и более оператора return, которые будут возвращать объекты разных типов, не связанных между собой иерархией наследования и не способные быть приведены к типу базового класса. И даже, если эти объекты имеют базовый класс, необходимо будет прописать тип возвращаемого значения, им как раз будет указатель на объект базового класса (в общем случае).

Вот пример кода, который не скомпилируется.

#include <iostream>

using namespace std;

class A
{
public:
    A(){}
};

class B : public A
{
public:
    B(){}
};

class C : public A
{
public:
    C(){}
};

int main()
{
    auto myLambda = [](int type)
    {
        if (type == 0)
        {
            return new B();
        }
        else
        {
            return new C();
        }
    };

    myLambda(0);

    return 0;
}

Нужно указать тип возвращаемого значения

auto myLambda = [](int type) -> A* // Укажем тип возвращаемого значения
{
    if (type == 0)
    {
        return new B();
    }
    else
    {
        return new C();
    }
};

Также ошибка компиляции будет в том случае, если не указать тип возвращаемого значения и при этом вы создаёте в куче объект внутри лямбда функции, но в некоторых случаях можете вернуть указатель на nullptr. То есть ниже следующий код не скомпилируется.

#include <iostream>

using namespace std;

class A
{
public:
    A(){}
};


int main()
{
auto myLambda = [](int type)
{
    if (type == 0)
    {
        return new A();
    }
    else
    {
        return nullptr;
    }
};

    myLambda(0);

    return 0;
}

Опять нужно указать тип возвращаемого значения

auto myLambda = [](int type) -> A* // Укажем тип возвращаемого значения
{
    if (type == 0)
    {
        return new A();
    }
    else
    {
        return nullptr;
    }
};

Дело в том, что nullptr - это универсальный тип данных, который в каком-то смысле не является типом данных, поскольку его нельзя установить в качестве типа переменной. Но он может быть присвоен в качестве значения указателю на объект. Чтобы неявное преобразование в данном случае происходило правильно, нужно также указать тип возвращаемого значения.

Также в выше приведённом примере показано, как вызвать лямда функцию и передать в неё параметры. Заметили? В данном примере используется параметр int type , в зависимости от которого мы возвращаем указатель на созданный объект или nullptr .

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

Список символов может быть передан следующим образом:

  • **[a,&b]**
    где a захвачена по значению, а b захвачена по ссылке.
  • **[this]**
    захватывает указатель
    **this**
    по значению.
  • **[&]**
    захват всех символов по ссылке
  • **[=]**
    захват всех символов по значению
  • **[]**
    ничего не захватывает

Про захват переменных поговорим в следующих статьях.

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

Например такой код тоже скомпилируется

#include <iostream>

using namespace std;

class A
{
public:
    A(){}
};


int main()
{

    A* myA = [](int type) -> A*
    {
        if (type == 0)
        {
            return new A();
        }
        else
        {
            return nullptr;
        }
    }(0);

    return 0;
}

Так что подумайте, скомпилируется ли следующий программный код?

int main(){[](){}();}
Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Ол саған ұнайды ма? Әлеуметтік желілерде бөлісіңіз!

Пікірлер

Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
Кіріңіз немесе Тіркеліңіз
Г

C++ - Тест 001. Первая программа и типы данных

  • Нәтиже:66ұпай,
  • Бағалау ұпайлары-1
t

C++ - Тест 001. Первая программа и типы данных

  • Нәтиже:33ұпай,
  • Бағалау ұпайлары-10
t

Qt - Тест 001. Сигналы и слоты

  • Нәтиже:52ұпай,
  • Бағалау ұпайлары-4
Соңғы пікірлер
G
GoattRockҚыр. 3, 2024, 1:50 Т.Қ.
Linux жүйесінде файлдарды қалай көшіруге болады Задумывались когда-нибудь о том, как мы привыкли доверять свои вещи службам грузоперевозок? Сейчас такие услуги стали неотъемлемой частью нашей жизни, особенно когда речь идет о переездах между …
d
dblas5Шілде 5, 2024, 11:02 Т.Ж.
QML - Сабақ 016. SQLite деректер қоры және онымен QML Qt-та жұмыс істеу Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
k
kmssrАқп. 8, 2024, 6:43 Т.Қ.
Qt Linux - Сабақ 001. Linux астында Autorun Qt қолданбасы как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий КононенкоАқп. 5, 2024, 1:50 Т.Ж.
Qt WinAPI - Сабақ 007. Qt ішінде ICMP Ping арқылы жұмыс істеу Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
Енді форумда талқылаңыз
Evgenii Legotckoi
Evgenii LegotckoiМаусым 24, 2024, 3:11 Т.Қ.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
F
FynjyШілде 22, 2024, 4:15 Т.Ж.
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …
BlinCT
BlinCTМаусым 25, 2024, 1 Т.Ж.
Нарисовать кривую в qml Всем привет. Имеется Лист листов с тосками, точки получаны интерполяцией Лагранжа. Вопрос, как этими точками нарисовать кривую? ChartView отпадает сразу, в qt6.7 появился новый элемент…
BlinCT
BlinCTМамыр 5, 2024, 5:46 Т.Ж.
Написать свой GraphsView Всем привет. В Qt есть давольно старый обьект дял работы с графиками ChartsView и есть в 6.7 новый но очень сырой и со слабым функционалом GraphsView. По этой причине я хочу написать х…
Evgenii Legotckoi
Evgenii LegotckoiМамыр 2, 2024, 2:07 Т.Қ.
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.

Бізді әлеуметтік желілерде бақылаңыз