Можно ли передать лямбда-функцию в качестве указателя на функцию? Если это так, я должен что-то делать неправильно, потому что я получаю ошибку компиляции.
Рассмотрим следующий пример
using DecisionFn = bool(*)();
class Decide
{
public:
Decide(DecisionFn dec) : _dec{dec} {}
private:
DecisionFn _dec;
};
int main()
{
int x = 5;
Decide greaterThanThree{ [x](){ return x > 3; } };
return 0;
}
Когда я пытаюсь скомпилировать это , я получаю следующую ошибку компиляции:
In function 'int main()':
17:31: error: the value of 'x' is not usable in a constant expression
16:9: note: 'int x' is not const
17:53: error: no matching function for call to 'Decide::Decide(<brace-enclosed initializer list>)'
17:53: note: candidates are:
9:5: note: Decide::Decide(DecisionFn)
9:5: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'DecisionFn {aka bool (*)()}'
6:7: note: constexpr Decide::Decide(const Decide&)
6:7: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'const Decide&'
6:7: note: constexpr Decide::Decide(Decide&&)
6:7: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'Decide&&'
Это одна черта сообщения об ошибке, чтобы переварить, но я думаю, что я получаю из этого, что лямбда не может рассматриваться как constexpr
так, поэтому я не могу передать его как указатель на функцию? Я тоже пытался сделать x
const, но это, похоже, не помогает.
c++
c++11
lambda
function-pointers
Кори Крамер
источник
источник
Ответы:
Лямбда может быть преобразована в указатель на функцию, только если она не захватывает, из черновика стандартного раздела C ++ 11
5.1.2
[expr.prim.lambda] сказано ( выделено мое ):Обратите внимание, что cppreference также описывает это в своем разделе о лямбда-функциях .
Таким образом, следующие альтернативы будут работать:
и так будет:
и, как указывает 5gon12eder , вы также можете использовать
std::function
, но учтите, чтоstd::function
это тяжелый вес , так что это не без затратный компромисс.источник
void*
единственного параметра. Обычно это называется «указатель пользователя». Он также относительно легкий, но, как правило, требует от васmalloc
свободного места.Ответ Шафика Ягмура правильно объясняет, почему лямбда не может быть передана как указатель на функцию, если она имеет перехват. Я хотел бы показать два простых решения проблемы.
Используйте
std::function
вместо сырых указателей функций.Это очень чистое решение. Однако обратите внимание, что он включает некоторые дополнительные издержки для стирания типа (возможно, вызов виртуальной функции).
Используйте лямбда-выражение, которое ничего не захватывает.
Поскольку ваш предикат на самом деле является просто логической константой, следующее быстро обойдет текущую проблему. Посмотрите этот ответ для хорошего объяснения, почему и как это работает.
источник
glutReshapeFunc
.std::function
или лямбду - почему бы и нет? По крайней мере, это более читаемый синтаксис. Обычно необходимо использовать указатель на функцию для взаимодействия с библиотеками C (фактически, с любой внешней библиотекой) , и вы не можете изменить его, чтобы он принимал std :: function или lambda.Лямбда-выражения, даже захваченные, могут быть обработаны как указатель на функцию (указатель на функцию-член).
Это сложно, потому что лямбда-выражение не простая функция. На самом деле это объект с оператором ().
Когда вы креативны, вы можете использовать это! Подумайте о классе «function» в стиле std :: function. Если вы сохраните объект, вы также можете использовать указатель на функцию.
Чтобы использовать указатель на функцию, вы можете использовать следующее:
Чтобы создать класс, который может начать работать как «std :: function», сначала вам нужен класс / структура, который может хранить указатель на объект и функцию. Также вам нужен оператор () для его выполнения:
Теперь вы можете запускать захваченные, не захваченные лямбды, как вы используете оригинал:
Этот код работает с VS2015
Обновление 04.07.17:
источник
operator()
реализацию, поэтому, если я правильно ее читаю, я не думаю, что будет работать вызов лямбды с использованием указателя на функцию в стиле C , не так ли? Это то, что задал оригинальный вопрос.Захват лямбды не может быть преобразован в указатели функций, как указывалось в этом ответе .
Однако зачастую довольно сложно предоставить указатель на функцию для API, который принимает только один. Наиболее часто упоминаемый метод для этого - предоставить функцию и вызвать статический объект с ней.
Это утомительно. Мы продолжаем эту идею и автоматизируем процесс создания
wrapper
и облегчаем жизнь.И использовать его как
Жить
По сути это объявляет анонимную функцию при каждом появлении
fnptr
.Обратите внимание, что вызовы
fnptr
перезаписывают ранее записанныеcallable
заданные вызовы того же типа. Мы исправим это, в определенной степени, с помощьюint
параметраN
.источник
Ярлык для использования лямбда с указателем на функцию C таков:
Использование Curl в качестве примера ( информация об отладке curl )
источник
+
помогает).Хотя шаблонный подход является умным по разным причинам, важно помнить о жизненном цикле лямбды и захваченных переменных. Если будет использоваться любая форма лямбда-указателя, и лямбда не является нисходящим продолжением, то должна использоваться только копирующая [=] лямбда. Т.е. даже тогда захват указателя на переменную в стеке НЕ БЕЗОПАСЕН, если время жизни этих захваченных указателей (разматывание стека) короче, чем время жизни лямбды.
Более простое решение для захвата лямбды в качестве указателя:
например,
new std::function<void()>([=]() -> void {...}
Просто не забудьте позже,
delete pLamdba
чтобы убедиться, что вы не пропустите лямбда-память. Секрет, который необходимо осознать, заключается в том, что лямбда-выражения могут захватывать лямбда-выражения (спросите себя, как это работает), а также, что для того,std::function
чтобы работать в целом, лямбда-реализация должна содержать достаточную внутреннюю информацию для обеспечения доступа к размеру лямбда-данных (и захваченных) данных ( именно поэтомуdelete
должен работать [запуск деструкторов захваченных типов]).источник
new
- std :: function уже хранит лямбда в куче И избегает необходимости запоминать вызов delete.Не прямой ответ, но небольшое изменение в использовании шаблона шаблона «функтор», чтобы скрыть специфику лямбда-типа и сделать код красивым и простым.
Я не был уверен, как вы хотите использовать класс решить, поэтому мне пришлось расширить класс с помощью функции, которая его использует. Смотрите полный пример здесь: https://godbolt.org/z/jtByqE
Основная форма вашего класса может выглядеть так:
Где вы передаете тип функции как часть типа класса, используемого как:
Опять же, я не был уверен, почему вы захватываете,
x
было больше смысла (для меня) иметь параметр, который вы передаете в лямбду), поэтому вы можете использовать как:Смотрите ссылку для полного примера
источник
Как уже упоминалось другими, вы можете заменить лямбда-функцию вместо указателя на функцию. Я использую этот метод в своем интерфейсе C ++ для решения F77 ODE RKSUITE.
источник