Указатель на лямбда функцию c

Обновлено: 05.07.2024

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

Рассмотрим следующий пример

Когда я пытаюсь скомпилировать это, я получаю следующую ошибку компиляции:

Лямбда может быть преобразована в указатель на функцию, только если она не захватывается из черновик стандарта C ++ 11 в разделе 5.1.2 [expr.prim.lambda] говорится ( выделено мной ):

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

Обратите внимание, что cppreference также рассматривает это в своем разделе лямбда-функций.

Итак, подойдут следующие альтернативы:

И, как указывает 5gon12eder, вы также можете использовать std::function , но учтите, что std::function имеет большой вес, так что это не дешевый компромисс.

Как уже упоминалось другими, вы можете заменить лямбда-функцию вместо указателя на функцию. Я использую этот метод в своем интерфейсе C ++ для решателя ODE F77 RKSUITE.

Аналогичный ответ, но я сделал это так, что вам не нужно указывать тип возвращаемого указателя (обратите внимание, что для общей версии требуется C ++ 20):

Хотя шаблонный подход является разумным по разным причинам, важно помнить о жизненном цикле лямбда-выражения и захваченных переменных. Если будет использоваться любая форма лямбда-указателя, и лямбда не является продолжением вниз, тогда следует использовать только копирующую [=] лямбда. То есть, даже тогда захват указателя на переменную в стеке НЕ БЕЗОПАСНЫЙ, если время жизни этих захваченных указателей (раскрутка стека) короче, чем время жизни лямбда.

Более простое решение для захвата лямбды в качестве указателя:

Например, new std::function ([=]() -> void

Просто не забудьте позже delete pLamdba , чтобы не допустить утечки лямбда-памяти. Секрет, который следует понять здесь, заключается в том, что лямбда-выражения могут захватывать лямбда-выражения (спросите себя, как это работает), а также что для того, чтобы std::function работал в общем случае, реализация лямбда-выражения должна содержать достаточную внутреннюю информацию для обеспечения доступа к размеру лямбда-выражения ( и захваченные) данные (вот почему delete должен работать [запускать деструкторы захваченных типов]).

Базовая форма вашего класса может выглядеть так:

Если вы передаете тип функции как часть используемого типа класса, например:

Опять же, я не был уверен, почему вы захватываете x , для меня было более разумным (для меня) иметь параметр, который вы передаете лямбда), поэтому вы можете использовать его, например:

См. Ссылку для полного примера

Ярлык для использования лямбды с указателем на функцию C следующий:

Использование Curl в качестве примера (информация об отладке curl)

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

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

Это утомительно. Мы развиваем эту идею дальше и автоматизируем процесс создания wrapper и делаем жизнь намного проще.

И использовать его как

По сути, это объявление анонимной функции при каждом появлении fnptr .

Обратите внимание, что вызовы fnptr перезаписывают ранее записанные callable заданные вызываемые объекты того же типа. Мы исправляем это в определенной степени с помощью параметра int N .

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

Это сложно, потому что лямбда-выражение - непростая функция. Фактически это объект с оператором ().

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

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

Этот код работает с VS2015

Ответ Шафика Ягмура правильно объясняет, почему лямбда не может быть передана в качестве указателя на функцию, если она имеет захват. Я хотел бы показать два простых решения проблемы.

Используйте std::function вместо необработанных указателей на функции.

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

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

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

Вся информация о функции лямбда в C ++: от C ++ 11 до C ++ 17

Функция лямбда введена в C ++ 11Modern C ++Интуитивно понятные понятия, поэтому есть много статей о руководстве на лямбдах в Интернете. Тем не менее, есть еще некоторые вещи, которые трудно сказать (например, iife, лямбда и т. Д.), Никто не говорит. Итак, здесь я не только покажу вам функцию лямбда в C ++, но и ввести внутренний рабочий режим Lambda и другие аспекты лямбда.

Эта статья немного вводит в заблуждение. Потому чтоЛямбда не всегда синтезируется как указатель функции 。 Это выражение (просто уникальное закрытие). Но для простоты я делал это. Поэтому, отныне, я поочередно использую функцию лямбда и выражения.

Что такое лямбда?

Функция лямбда - это фрагмент короткого кода.

  • Не стоит назвать (неназванные, анонимные, ручка и т. Д.), Независимо от того, как вы это называете,
  • И это не будет повторно использовано.

Другими словами, это просто синтаксис. Синтаксис, определяемый функцией лямбда, - это:

  • обычно, Компилятор оценит возвратный тип самой функции лямбда. 。 Поэтому нам не нужно явно указать тип задней возврата, а именно -> return-type 。
  • Однако в некоторых сложных ситуациях компилятор не может выводить тип возврата, поэтому нам нужно указать тип возврата.

Почему мы должны использовать функцию лямбда?

  • C ++ включает в себя много полезных общих функций, таких как std::for_each Они очень удобны. К сожалению, они также очень неприятно, особенно если вы хотите применить функцию, уникальную для определенных функций. Рассмотрим следующий код в качестве примера:
  • Если вы используете только печать в этом конкретном месте, то приготовьте целый класс, просто чтобы завершить некоторые тривиальные и одноразовые вещи, кажется, слишком высоки.
  • Однако для этой ситуации встроенный код будет более подходящим и может быть реализован следующей функцией Lambda:

Как работает лямбда функция работает внутренней?

  • Компилятор генерирует единственный закрытый пакет для каждой функции лямбда 。 Наконец, секрет был раскрыт.
  • Список захвата станет параметрами конструктора в закрытии. Если вы захватите параметры в качестве значения, создайте соответствующий тип данных в закрытии.
  • Кроме того, вы можете объявить переменные / объекты в параметрах функции Lambda, которые станут параметрами оператора вызова, а именно operator() 。

Преимущества использования функции лямбда

  • Сделано из нуля. Да! Вы не видели это неправильно. Лямбда не будет жертвовать вашим выступлением, и не будет так быстро, как нормальная функция?
  • Кроме того, код становится компактным, структурированным и сильным.

Учебное выражение лямбда

Захват по ссылке / значению

  • В приведенном выше примере я упоминал в списке захвата. & 。 Он захватывает переменные x с y Справка. Сходным образом, = Указывает, что значение захвата, которое создаст данные одинаковых данных в закрытии и распределение копирования.
  • Обратите внимание, что список параметров не является обязательным.Если вы не передаете параметрыДать лямбда выражение, затемМожет сохранить пустые скобки

Список захвата лямбда


Проезжая лямбда как параметр

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

Переменные улавливания элементов в лямбде или в этом указателе

  • pointer can also be captured using [this] , [=] or [&] . In any of these cases, class data members(including ) can be accessed as you do in a normal method.
  • Если вы видите лямбда выражение, я использую дополнительные в конце функции лямбда. () Эта функция называется после декларации. Это называетсяIIFE ( Вызовите функцию выражения немедленно )。

Тип функции лямбда C ++

  • General Lambda, представленные в C ++ 14, могут быть использованы auto Параметры захвата спецификации.

Переменные параметры универсальные λ

  • Лямбда с переменными пакетами параметров очень полезна во многих случаях, таких как отладка, дубликата операции с использованием разных входов данных.

mutable λ функция

  • Обычно оператор вызова функции лямбда - это значение const - это значение, что означает, чтоЕсли вы хотите захватить любое содержимое, захваченное значением, лямбда должен использовать переменное ключевое слово.
  • Выше мы видели пример. Я надеюсь, что вы заметили.

Лямбда как указатель функции

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

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

  • Кроме того, функция Lambda также может вернуть другую функцию лямбда. Это откроет бесконечно возможное дверь для кода, кода и сжимаемости Открыты неограниченные возможные двери (в виде способа, нет такого слова).

constexpr Лямбда выражение

  • Начиная с C ++ 17, выражение лямбда может быть объявлено какconstexpr 。
  • Даже если вы не укажете constexpr Оператор вызова функции constexpr Во всяком случае, если это происходит, чтобы встретить всеФункциональные требования CONSTEXPR 。

Вывод

Я надеюсь, тебе нравится эта статья. Я пытался использовать несколько простых небольших примеров для введения больших сложных проблем лямбда. Принимая во внимание экспрессию кода и простого обслуживания, вы должны использовать Lambda в любом месте, так же, как вы можете использовать его в Smart Complers и большинству алгоритмов STL в пользовательском удалении.

Лямбда-функции появились в 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.

Вы можете заказать у нас

VPS (Virtual Private Server, виртуальный выделенный сервер, другое название VDS — Virtual Dedicated Server) — стартовая площадка для крупных проектов, позволяющая более рационально использовать представляемые ресурсы в рамках выбранного тарифа OpenVZ-сервера. OpenVZ — технология виртуализации c общим ядром основной операционной системы без эмуляции отдельного физического сервера. Предлагает меньшую стоимость за счёт низких накладных расходов на виртуализацию. KVM — аппаратная виртуализация, при которой полностью эмулируется физический сервер, что позволяет устанавливать любые операционные системы (Linux, Windows, FreeBSD и другие) и дает гарантированные ресурсы физического сервера. Для всех серверов предоставляется надежная DDOS-защита.

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