Лямбда функции c qt

Добавил пользователь Дмитрий К.
Обновлено: 19.09.2024

В этой статье описывается каким образом изменения, принесенные стандартом С++14, отразились или могут отразиться на разработке Qt приложений. Данная статья ориентирована не только на Qt программистов, но также на всех тех, кому интересно развитие С++. Автор оригинала — Olivier Goffart, являющийся одним из разработчиков Qt moc (meta-object compiler).

Обобщенные лямбда-функции

В С++11 были введены лямбда-функции, и Qt5 позволяет использовать их в сигналах. C++14 упрощает использование лямбда-функций, так как теперь тип аргументов может быть выведен автоматически, то есть стало возможным использование auto в качестве типа параметра вместо того, чтобы явно описывать этот тип:

Лямбда-функция представляет собой функтор с реализованным оператором operator(). В обобщенных лямбда-функциях этот оператор объявлен как шаблонная функция. Я сделал изменения, которые поддерживают такие функторы и эти изменения были включены в Qt 5.1. C++14 также добавляет возможность захвата не только переменных, но и выражений:

Смягчение требований к константным выражениям

В С++11 было введено новое ключевое слово constexpr. В Qt 4.8 был введен новый макрос Q_DECL_CONSTEXPR, который разворачивается в constexpr, если это слово поддерживается компилятором. В Qt 5 этот макрос используется для большого количества функций, где это только представляется возможным.
В С++14 были смягчены требования, предъявляемые к константным выражениям. С++11 позволял использовать constexpr только с единственным оператором возврата и только в функциях-членах с модификатором const. С++14 позволяет намного больше, лишь бы вычисление могло происходить во время компиляции.

Функции-члены класса, объявленные как constexpr в С++11 автоматически трактуются как константные, то есть, не изменяющие поля класса. В С++14 неконстантная функция-член класса тоже может быть constexpr. Результатом такого изменения стало то, что функции-члены классов, объявленные constexpr, но не имеющие явно указанного модификатора const, стали неконстантными в С++14, а это означает несовместимость на уровне бинарных файлов. К счастью, в Qt макрос Q_DECL_CONSTEXPR явно объявлял все функции-члены классов константными, поэтому никакого нарушения бинарной совместимости при его использовании нет.
Итак, теперь мы можем вычислять во время компиляции неконстантные функции классов, такие, например, как operator=. Для этого в Qt 5.5 будет введен новый макрос Q_DECL_RELAXED_CONSTEXPR, который будет разворачиваться в constexpr, если компилятор в режиме С++14.

Небольшие изменения в С++14

Стандарт С++14 привнес некоторое количество небольших изменений, цель которых — сделать разработку более удобной. Эти изменения не имеют непосредственного влияния на Qt, но вполне могут быть использованы в Ваших программах, если используется компилятор с поддержкой С++14.

Разделители разрядов чисел

Если нужно определить большую константу в коде, можно использовать апостроф в качестве разделителя разрядов:

Двоичные константы

В С++ можно определять десятичные, восьмеричные (начинающиеся с 0), и шестнадцатеричные (начинающиеся с 0x) константы. Теперь появилась возможность определять и двоичные константы, используя префикс 0b:

Автоматический вывод типа возвращаемого значения

Если у Вас есть встроенная (inline) функция, то Вы можете использовать ключевое слово auto в качестве указания возвращаемого типа, его теперь можно не указывать явно. Компилятор сам его выведет:

Это, к сожалению, не поддерживается для Qt слотов или так называемых invocable методов, так как Qt moc не в состоянии сам определить возвращаемый тип.

Шаблонные переменные

Раньше было возможным сделать шаблонную функцию или класс. Сейчас можно сделать шаблонной и просто переменную.

Инициализация структур

В С++11 стало возможно инициализировать структуру, у которой нет определенного пользователем конструктора, списком инициализации (значения полей в фигурных скобках), а также появилась возможность присваивать нестатическим полям класса значения по умолчанию прямо в определении класса. Но в С++11 нельзя было использовать сразу оба этих варианта инициализации. В С++14 теперь можно. Этот код будет работать именно так, как и ожидается:

Квалификаторы ссылок для методов классов

Это на самом деле было привнесено не в С++14, а еще в С++11, но мы начали использовать эти квалификаторы только в Qt5, и я не упоминал о них в предыдущих постах, поэтому поговорим о них сейчас.
Рассмотрим следующий код:

Здесь fromUtf8 возвращает временную переменную. Было бы неплохо, если бы метод toLower использовал уже выделенную память для этой временной переменной и выполнил в ней необходимые преобразования. Именно для подобных случаев и были введены квалификаторы ссылок для функций-членов классов.
Упрощенный код из qstring.h:

Обратите внимание на '&' и '&&' в конце методов toLower. Это квалификаторы ссылок и позволяют перегрузить функцию в зависимости от типа, на который указывает 'this', таким же образом, как квалификатор const позволяет перегрузить метод в зависимости от константности 'this'. В случае, когда toLower вызывается для временной переменной (rvalue) будет выбран второй метод (который с &&) и проведет изменения строки, не копируя ее.
Функции, которые были улучшены с помощью этих квалификаторов в Qt 5.4: QString::toUpper, QString::toLower, QString::toCaseFolded, QString::toLatin1, QString::toLocal8Bit, QString::toUtf8, QByteArray::toUpper, QByteArray::toLower, QImage::convertToFormat, QImage::mirorred, QImage::rgbSwapped, QVersionNumber::normalized, QVersionNumber::segment

Изменения в стандартной библиотеке

С++11 и С++14 добавили много конструкций в стандартную библиотеку, которые во многом перекликаются с имеющимися конструкциями в QtCore. В Qt стандартная библиотека используется очень мало. Мы вообще не хотим, чтобы стандартная библиотека была частью ABI. Это позволит оставаться бинарно совместимыми даже если стандартная библиотека изменится (например libstdc++ и libcpp). Также Qt до сих пор поддерживает некоторые старые платформы, на которых нет стандартной библиотеки С++11. По этим причинам мы ограничиваем использование этой библиотеки.
Но есть исключение — Qt5 объявляет свои алгоритмы устаревшими (deprecated) и сейчас рекомендуется использовать алгоритмы STL (например std::sort вместо qSort).

Заключение


Конечно, может пройти какое-то время, прежде чем Вы сможете использовать новые конструкции С++14 в своих проектах. Но я надеюсь, что вы начнете их применять как и многие другие (Qt Creator, KDE, LLVM). В новых компиляторах MSVC C++14 активен по умолчанию, в clang и gcc нужно использовать специальный флаг (на настоящий момент это -std=c++1y). С помощью qmake можно настроить свой проект на сборку с С++14 начиная с Qt5.4 используя следующую команду: CONFIG += c++14

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

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

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

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

  • 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 .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Рекомендуем хостинг TIMEWEB

Рекомендуем хостинг TIMEWEB

Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

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

А если учесть ещё и перегрузку map() и mapped() , то это делает код с QSignalMapper ещё более страшным, если использовать коннекты сигналов и слотов с использованием указателей, поскольку необходимо кастовать как сигналы, так и слоты, но об этом чуть позже.

Поэтому давайте рассмотрим небольшой проект, который будет основываться на примере из официальной документации Qt. А именно, пример будет следующий. У нас имеется QLabel, QPushButton и Vertical Layout . По нажатию кнопки в Vertical Layout будут добавляться другие динамические кнопочки, по нажатию на которые в QLabel будет отображаться текст с номером кнопки в следующем виде: "Button 2". На ниже следующем рисунке показан пример данного приложения, внешний вид которого не будет отличаться, тогда как реализаций программного кода будет несколько.

  1. 1. Вариант 1 - QSignalMapper и синтаксис на макросах
    1. 1. mainwindow.h
    2. 2. mainwindow.cpp
    1. 1. mainwindow.h
    2. 2. mainwindow.cpp
    1. 1. mainwindow.h
    2. 2. mainwindow.cpp



    Вариант 1 - QSignalMapper и синтаксис на макросах

    Для начала разберём вариант, который предлагается в качестве примера в официальной документации. Когда сигналы и слоты подключаются с помощью макросов, то есть вариант, который совместим с Qt 4.8.

    Внешний вид приложения создавался в графическом дизайнере, поэтому не удивляйтесь использованию ui объекта.

    mainwindow.h

    Итак, чтобы приложение заработало, нам понадобится QSignalMapper и слот, в котором будет обрабатываться нажатие динамической кнопки. Нюанс в том, что в QLabel будет устанавливаться текст из этой самой кнопки, поэтому воспользуемся сигналом QSignalMapper::mapped(const QString &) , который будет приниматься слотом MainWindow::clicked(const QString &), в данном слоте виджет будет преобразован в объект QPushButton, и мы заберём из него текст. Текст будет предварительно устанавливаться при создании данной кнопки. Для нумерации будет использоваться счётчик созданных кнопок (переменная int counter).

    mainwindow.cpp

    И рассмотрим, как всё это выглядит в коде.

    Вариант 2 - QSignalMapper и новый синтаксис

    В первом варианте код совместим с кодом на Qt 4.8 и в целом достаточно читабелен, но что если мы не собираемся поддерживать проект на версии 4.8? Тогда первое, что нужно сделать, это переписать данный код с использованием синтаксиса на указателях и сделать небольшие вкрапление лямбда функций. И тогда вы увидите, что я подразумевал, говоря, что QSignalMapper будет выглядеть как монструозный атавизм.

    mainwindow.h

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

    mainwindow.cpp

    Посмотрим, как теперь выглядит программный код. Казалось бы, получаем однозначный WIN:

    1. Убрали один слот, благодаря лямбда функции;
    2. Получили возможность отслеживать ошибки уже на этапе компиляции, а не в рантайме, чем грешат макросы сигналов и слотов;
    3. Привели код к стандарту Qt5.

    Вариант 3 - избавляемся от QSignalMapper

    А теперь избавимся от QSignalMapper. Ведь если использовать все возможности лямбда функций, то для реализации таких задач, как в данном примере, QSignalMapper вовсе не нужен.

    mainwindow.h

    Код в заголовочном файле немного сократился, как видите.

    mainwindow.cpp

    И как можете видеть, использование лямбда функции значительно сокращает код, а также избавляет в данной ситуации от использования класса QSignalMapper вовсе. Вполне возможно, что данный класс благодаря развитию самого языка C++ отомрёт вовсе.

    Резюмируем .

    Применение нового синтаксиса сигналов и слотов, позволяет подключить мощные возможности языка C++ и в применении к Qt вовсе избавиться от использования некоторых классов, польза которых была несомненной в устаревших версиях Qt. А также использование лямбд позволяет значительно сократить и упростить программный код.

    Рекомендуем хостинг TIMEWEB

    Рекомендуем хостинг TIMEWEB

    Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

    Рекомендуемые статьи по этой тематике

    По статье задано0 вопрос(ов)

    Подписчики

    Платёжная система

    C:\Projects\Qt\BMR-1401LM-U\dialogfeatures.cpp:31: ошибка: no matching function for call to 'DialogFeatures::connect(QComboBox*&, , DialogFeatures::DialogFeatures(int, int, int, int, int, int, QWidget*)::)'

    Не можете подсказать почему?

    currentIndexChanged является сигналом с перегрузкой. Поэтому его нужно подключать через static_cast с указанием сигнатуры сигнала.

    Вот такая петрушка получается:

    Спасибо! Знал же, что сигнал с перегрузкой. Думал : "А как компилятор поймет какую именно вызывать?",- но не знал такой прием как указать сигнатуру через static_cast :)

    Этот приём в официальной документации на QComboBox указан ;-)

    Лямбда удобная штука. Только вчера научился, мне нравится :)

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

    А писать слот - это значит, что слот может быть вызван из вне. А это не всегда нужно. Я искал про private slots, думал, что private slot может быть связан только с сигналом своего класса, но я ошибался.

    По сути лямбда ограничивает область видимости , но нарушает принцип qt сигнал-слот. Это так?

    Был бы рад услышать твое мнение по этой теме :)

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

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

    В примере нужно получить сигнал от кнопки, и что-то сделать с той же самой кнопкой. Если использовать слот, то придётся держать какой-то вектор объектов в заголовочном файле, либо как сделано здесь, использовать QSignalMapper, а это опять же разрастание кода и повышение избыточности кода. А так лямбда захватывает кнопку и спокойно выполняет необходимый код. Когда кнопка будет уничтожена, а память освобождена, то лямбда автоматически будет отключена от сигнала этой кнопки и так же будет уничтожена. Так что проблем здесь никаких не возникнет.

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

    Плюс некоторые возможности шаблонизации, которые присущи лямбдам с аргументами auto. Плюсов слишком много, чтобы игнорировать использование лямбд в сигналах и слотах.

    У многих программистов, работающих с Qt4, наверняка возникало навязчивое желание соединить сигнал, посылаемый неким наследником QObject, c простой функцией, не являющейся слотом или даже членом некоторого класса. Однако если весь проект построен на объектах (как обычно и бывает), да и все они наследуются от QObject, то добавить функцию-слот куда надо не составит труда. А если нет? Если вы, например, из экономии памяти (или по другим соображениям) не хотите наследовать ваш класс от QObject, или же действие слота занимает всего 1 строчку и было бы проще и удобнее написать его в виде лямбда-выражения? Или вы по ряду причин хотите по сигналу вызывать одиночную фунцию, не являющуюся членом класса?

    Столкнувшись с этой проблемой, я решил написать класс, который позволяет соединять сигнал не только с функцей-слотом, а ещё и с самой обычной функцией, а при поддержке С++11 — ещё и с лямбда-выражением.

    Пишем класс SmartConnect

    Для начала — что должен делать класс? Он должен наследоваться от QObject и хранить ссылку на нашу функцию. Т.е. мы связываем сигнал какого-либо класса с некоторым слотом SmartConnect, а уже SmartConnect хранит ссылку на нашу независимую функцию или лямбда-выражение, и вызывает его по своему слоту.

    Самым удобным решением будет перегрузить конструктор SmartConnect — для ссылки на разные функции. Пускай он для начала работает двумя типами сигналов — которые в аргументе передают void, а также те, которые передают QString. Создадим файл smartconnect.h:

    class SmartConnect : public QObject
    <
    Q_OBJECT
    void ( * pVoidFunc ) ( void ) ;
    void ( * pQStringFunc ) ( QString ) ;
    public :
    SmartConnect ( QObject * sender , const char * signal , void ( * pFunc ) ( void ) ) ;
    SmartConnect ( QObject * sender , const char * signal , void ( * pFunc ) ( QString ) ) ;
    private slots :
    void voidSlot ( ) ;
    void QStringSlot ( QString str ) ;
    > ;

    SmartConnect :: SmartConnect ( QObject * sender , const char * signal , void ( * pFunc ) ( ) ) <
    pVoidFunc = pFunc ;
    QObject :: connect ( sender , signal , this , SLOT ( voidSlot ( ) ) ) ;
    >

    SmartConnect :: SmartConnect ( QObject * sender , const char * signal , void ( * pFunc ) ( QString ) ) <
    pQStringFunc = pFunc ;
    QObject :: connect ( sender , signal , this , SLOT ( QStringSlot ( QString ) ) ) ;
    >

    void SmartConnect :: voidSlot ( ) <
    pVoidFunc ( ) ;
    >

    void SmartConnect :: QStringSlot ( QString str ) <
    pQStringFunc ( str ) ;
    >

    Как видите, класс действительно smart — в зависимости от вызванного конструктора сам выбирает нужный внутренний слот и соединяется с ним, а также сохраняет ссылку на нашу функцию. При желании мы можем сделать поддержку сигналов с любыми аргументами — просто добавить ссылку на функцию с этими аргументами, слот, который их принимает и конструктор. А вот внешне использовать класс будет всё также просто.

    Пишем демонстрационный пример

    int main ( int argc , char * argv [ ] ) <
    QApplication app ( argc , argv ) ;

    QPushButton button1 ( "button1" ) ;
    button1. show ( ) ;
    SmartConnect smartConnect1 ( & button1 , SIGNAL ( clicked ( ) ) , onClick ) ;

    QT += gui
    TEMPLATE = app
    CONFIG += release
    SOURCES += main. cpp
    smartconnect. cpp
    HEADERS += smartconnect. h
    CONFIG += console
    QMAKE_CXXFLAGS += - std = gnu ++ 11


    При желании, класс можно усовершенствовать, например добавить в него disconnect, если нужно. Как видите — использование класса просто и удобно, в некоторых случаях он может очень пригодится и реально упростить код.

    QSignalMapper is a wonderful class to organize the work of the signals and slots dynamically created objects. For example, the dynamically created buttons or objects in QStackedWidget. This was particularly true in older versions of the software, that is, and relied on Qt 4.8, which signals and slots system was based on the use of macros. But in the current realities of the new syntax in the indexes is much more convenient, and also supports a lambda function that can afford and does get rid of the application QSignalMapper, which will look like a monstrous throwback to new projects that use the latest version of the framework Qt and C++ language standards.

    And when you consider more and congestion map() and mapped(), then it does with QSignalMapper code even more terrible, if used to connect signals and slots with the use of pointers, since it is necessary to cast the signals and slots, but more on that later.

    Therefore, let us consider a small project that will be based on the example of the official documentation of Qt. Namely, an example will next. We have QLabel, QPushButton and Vertical Layout . By pressing a button in the Vertical Layout will be added to other dynamic buttons, by clicking on which QLabel will display the text from the button number in the following form: "Button 2". In the following figure shows an example of the application whose appearance will not differ, while the software implementations of the code will be a few.

    1. 1. Variant 1 - QSignalMapper and macro syntax
      1. 1. mainwindow.h
      2. 2. mainwindow.cpp
      1. 1. mainwindow.h
      2. 2. mainwindow.cpp
      1. 1. mainwindow.h
      2. 2. mainwindow.cpp



      Variant 1 - QSignalMapper and macro syntax

      To begin Let us examine the option, which is offered as an example in official documentation. When the signals and slots are connected via macros, that is an option that is compatible with Qt 4.8.

      The appearance of the application created in the Graphics Designer, so do not be surprised to use ui object.

      mainwindow.h

      So that the application to work, we need QSignalMapper and slot in which pressing the dynamic key is processed. Nuance that will be installed in QLabel text of this same button, so use the signal QSignalMapper::mapped(const QString &) , which will be made a slot MainWindow::clicked(const QString &) , the slot widget will be transformed into an object QPushButton, and we zaberёm of his text. The text will be pre-installed with the creation of this button. For numbering counter created buttons (variable int counter) will be used.

      mainwindow.cpp

      And look at how all this looks in code.

      Variant 2 - QSignalMapper and new syntax

      In a first embodiment, the code is compatible with the code on the Qt 4.8 and generally quite readable, but what if we are not going to support the project to version 4.8? Then the first thing to do is to rewrite the code using the syntax on signs and making small patches of lambda functions. And then you'll see what I mean by saying that QSignalMapper will look like a monstrous throwback.

      mainwindow.h

      Now in the header file has no slot for processing dynamic button pressing, as is already the lambda will be used here.

      mainwindow.cpp

      Let's see how the code looks now. It would seem simple to get WIN:

      1. We removed one slot through a lambda function;
      2. We got to track errors already at compile time, rather than runtime than the sin signal macros and slots;
      3. Led code to Qt5 standard.

      But this code looks quite scary, because it is used for static_cast QSignalMapper signals and slots. This is due to the fact that as a slot map() , and mapped() signal is overloaded, and the compiler need to specify their signature. And below these design brevity and beauty the code does not give. But this situation can be corrected - consider the third option.

      Variant 3 - delete QSignalMapper

      Now get rid of QSignalMapper. After all, if you use all the features of the lambda functions to the realization of these objectives, as in this example, QSignalMapper not needed.

      mainwindow.h

      The code in the header file declined slightly, as you can see.

      mainwindow.cpp

      And as you can see, the use of lambda functions considerably reduces the code and saves in the given situation the use QSignalMapper class at all. It is possible that this class due to the development of the C ++ language wither away altogether.

      Conclusion .

      Application of the new syntax signals and slots, allowing you to connect the powerful features of C ++ and Qt application to completely get rid of the use of certain classes, the use of which was doubtless in older versions of Qt. As well as the use of lambdas can significantly reduce and simplify the code.

      We recommend hosting TIMEWEB

      We recommend hosting TIMEWEB

      Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.

      Recommended articles on this topic

      By article asked0 question(s)

      Subscribers

      Payment system

      C:\Projects\Qt\BMR-1401LM-U\dialogfeatures.cpp:31: ошибка: no matching function for call to 'DialogFeatures::connect(QComboBox*&, , DialogFeatures::DialogFeatures(int, int, int, int, int, int, QWidget*)::)'

      Не можете подсказать почему?

      currentIndexChanged является сигналом с перегрузкой. Поэтому его нужно подключать через static_cast с указанием сигнатуры сигнала.

      Вот такая петрушка получается:

      Спасибо! Знал же, что сигнал с перегрузкой. Думал : "А как компилятор поймет какую именно вызывать?",- но не знал такой прием как указать сигнатуру через static_cast :)

      Этот приём в официальной документации на QComboBox указан ;-)

      Лямбда удобная штука. Только вчера научился, мне нравится :)

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

      А писать слот - это значит, что слот может быть вызван из вне. А это не всегда нужно. Я искал про private slots, думал, что private slot может быть связан только с сигналом своего класса, но я ошибался.

      По сути лямбда ограничивает область видимости , но нарушает принцип qt сигнал-слот. Это так?

      Был бы рад услышать твое мнение по этой теме :)

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

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

      В примере нужно получить сигнал от кнопки, и что-то сделать с той же самой кнопкой. Если использовать слот, то придётся держать какой-то вектор объектов в заголовочном файле, либо как сделано здесь, использовать QSignalMapper, а это опять же разрастание кода и повышение избыточности кода. А так лямбда захватывает кнопку и спокойно выполняет необходимый код. Когда кнопка будет уничтожена, а память освобождена, то лямбда автоматически будет отключена от сигнала этой кнопки и так же будет уничтожена. Так что проблем здесь никаких не возникнет.

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

      Плюс некоторые возможности шаблонизации, которые присущи лямбдам с аргументами auto. Плюсов слишком много, чтобы игнорировать использование лямбд в сигналах и слотах.

      Читайте также: