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

Обновлено: 08.07.2024

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

Однако компилятор жалуется, когда я это делаю:

Изменение Functor на QtPrivate::Functor дает:

В принципе, все, что я хочу сделать, это передать аргумент, который QObject::connect будет доступен для моей функции. Какой тип мне нужно использовать?

Есть два варианта:

Первый вариант просто перенаправляет любой аргумент в connect , второй использует указатель полиморфной функции из стандартной библиотеки С++ 11. Аргумент шаблона должен соответствовать сигнатуре сигнала. Сигналы Qt недействительны,/ArgumentTypes. /необходимо заменить на список аргументов функции. Поэтому, если сигнал объявлен как

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

Приведу пример простейшей лямбда функции:

Выражение auto myLambda означает объявление переменной с автоматически определяемым типом. Крайне удобная конструкция C++11, которая позволяет сделать ваш код более лаконичным и устойчивым к изменениям. Настоящий тип лямбда-функции слишком сложный, поэтому набирать его нецелесообразно.

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

В качестве аргумента она принимает любой объект, который можно вызвать с аргументом -5. Более подробно о создании таких функций мы говорили, когда рассматривали указатели на функции в C++. Мы будем передавать в call() наши лямбда-функции для запуска.

Сначала просто выведем переданное лямбда-функции значение:

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

Обратите внимание на побочный эффект от связывания переменных с лямбда-функцией по ссылке:

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

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

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

Допустимо и комбинирование:

Однако замечу, что на практике лучше не использовать обобщенное привязывание через = и &, а явно обозначать необходимые переменные по одной. Иначе могут возникнуть загадочные ошибки из-за конфликтов имен.

Когда использовать лямбда-функции?

Один из лучших примеров правильного использования лямбда-функций связан с библиотекой алгоритмов stl. Большинство функций этой библиотеки принимают аргумент-предикат. Такой аргумент позволяет контролировать те или иные аспекты алгоритма.

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

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

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

Лямбда-функции в C++: Использовать или нет?

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

Меньше кода — проще программа

И начнем с простого примера. Пусть имеется функция-генератор, которая принимает любой вызываемый объект:

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

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

При этом для случая ООП-полиморфного решения понадобится класс-Адаптер для функции. Но это лишний код.

Возможна еще одна ситуация. Пусть в качестве аргумента someGenerator() требуется передать функцию-член. Возникает трудность:

Здесь я предлагаю сослаться на TDD и гибкие методики разработки ПО в целом. Первое приближение программы должно быть самым простым и кратким. Если понадобится что-то большее, сделаем рефакторинг в будущем. Но не раньше.

В качестве предварительного решения закончим наш класс, воспользовавшись лямбда-функцией следующим образом:

В замыкание лямбда-функции мы включили указатель this . Таким образом, для лямбда-функции доступны все поля экземпляра класса MyClass . В том числе и processorFunc() , который мы и вызываем.

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

Чем меньше область видимости, тем лучше

Продолжим работу над предыдущим примером. Реализацию, на которой мы остановились, можно упростить еще больше:

Она стала не только проще, но и надежнее. Но почему? Теперь лямбда-функции доступна лишь очень малая часть информации объекта MyClass . Теперь она не может случайно испортить его состояние.

Хорошей практикой программирования является обеспечение минимальной области видимости для переменных. Несколько примеров:

Чем меньше мест, откуда доступна переменная, тем код надежнее, поскольку сокращается число возможностей случайно испортить состояние объекта. Сначала нужно поместить переменную в блок <>. Если этого не хватает, то расширить область видимости до локальной на уровне функции. Если и этого мало, то превратить переменную в private-поле класса. Все, что дальше, уже делать опасно.

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

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

Отсюда следует второй аргумент в пользу лямбда-функций. Если некий простой алгоритм нужен только в одном узком месте, то нет смысла вытаскивать его на поверхность. Достаточно обойтись соответствующей лямбда-функцией. Превратить лямбда-функцию в полноценную функцию или функтор никогда не поздно. Вы легко поймете, если это понадобится, чтобы не нарушать принцип DRY.

Но я не мог найти правильный синтаксис для вызова слота.

1 ответ

я хочу построить указатель на слот Qt: union < void (*set_slot)(unsigned long value); void (*refresh_slot)(void); >the_slot; Определение слота таково: void set_pwm(unsigned long new_pwm); Я пытаюсь сделать что-то вроде этого: the_slot.set_slot = set_pwm; Но компилятор говорит, что подпись не.

У меня есть базовый класс, который определяет слот Qt class Base < public: Base() < connect(otherobject, SIGNAL(mySignal), this, SLOT(mySlot)); >public slots: virtual void mySlot() <> > Подкласс а просто реализует некоторые другие вещи. Подкласс B переопределяет слот class SubB : Base < public.

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

Редактировать: Приведенное выше решение работает только в том случае, если mMyObject объявлено в той же области createShortcuts , что и ниже, безопаснее.

Похожие вопросы:

Предполагая следующий сигнал PyQt: value_changed = pyqtSignal(value) Есть ли способ написать слот lambda, который присваивал бы значение, предоставляемое сигналом, определенной переменной, как в.

Я новичок в мире Qt) Я создал новое приложение Qt в MSVC 2008 году Использование Qt Creator добавленных элементов управления мне нужно, и один из них-QMenuBar Как я понимаю, эквивалент.

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

я хочу построить указатель на слот Qt: union < void (*set_slot)(unsigned long value); void (*refresh_slot)(void); >the_slot; Определение слота таково: void set_pwm(unsigned long new_pwm); Я пытаюсь.

У меня есть базовый класс, который определяет слот Qt class Base < public: Base() < connect(otherobject, SIGNAL(mySignal), this, SLOT(mySlot)); >public slots: virtual void mySlot() <> > Подкласс а.

Добавить слот для сигнала виджета несложно: щелкните по нему правой кнопкой мыши и выберите go to slot. Но как я могу удалить слот, созданный для кнопки (например, on_pushButton_clicked )из.

Я пытаюсь это сделать: boost::signals::connection c = somesignal.connect( [c]()->void< // Do something c.disconnect(); >) Вызовет ли это проблемы? Соединение c назначается только после.

Я написал простой калькулятор QT в VS2013. Я использовал сигнал released() , чтобы вызвать свои слоты, но мой слот не работает. Может быть, мой сигнал так и не сработал. Я новичок в QT, и я не знаю.

Я спроектировал QTableWidget с QPushButton , я хотел бы соединить эти кнопки с прорезью, чтобы скрыть некоторые строки. Я использую выражение lambda для передачи числа строки. Но компилятор не.

В данном коде я создаю лямбда-функцию, которая копирует QSharedPointer , а затем этот указатель и сама лямбда передаются в функцию, которая выполняет некоторые операции в другом потоке и по окончании вызывает эту самую лямбду. Суть в том, что если написать так:

то QSharedPointer , судя по всему не сбрасывает счётчик ссылок, и данные, на которые он указывал, не удаляются. Однако если написать вот так:

То всё окей и память освобождается. В чём дело - не пойму. Видимо, тут что-то связано с особенностями лямбда-функций.


1 ответ 1

Вы выполняете захват по значению, т.е. передаете в лямбду копию watcher . Естественно, никто эту копию не освобождает, это должна делать сама лямбда.

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

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