#c #c 11 #lambda
#c #c 11 #лямбда
Вопрос:
Например, я могу определить лямбда-функцию как
auto f = [](double value) {double ratio = 100; return value * ratio;}
Теперь я хочу сгенерировать функцию с отношением аргумент и вернуть лямбда-функцию, например
auto makeLambda(double ratio) { return [=](double value) {return value * ratio;}; }
Как это сделать?
Комментарии:
1. Что не так с тем, что у вас есть?
2. @chris: для определения типа возвращаемой функции требуется C 14.
Ответ №1:
С функцией C 14, возвращающей вывод типа, это должно сработать.
В C 11 вы могли бы определить другую лямбда-функцию (которая может определять возвращаемый тип), а не функцию (которая не может):
auto makeLambda = [](double ratio) {
return [=](double value) {return value * ratio;};
};
Как отмечено в комментариях, это можно было бы дополнительно обернуть, если вам особенно нужна функция:
auto makeLambdaFn(double ratio) -> decltype(makeLambda(ratio)) {
return makeLambda(ratio);
}
Комментарии:
1. Вы забыли сделать внутренний лямбда-захват.
2. Пример работает в idone, но не удался в vc2013. VC2013 по-прежнему недостаточно для c 11.
3. Если вам нужна функция, а не лямбда-выражение, снова оберните лямбда-выражение в функцию:
auto func(double x)->decltype(makeLambda(x)){return makeLambda(x);}
Ответ №2:
@disclaimer: этот ответ просто добавляет дополнительные аргументы к ответу @Slava (но слишком длинный для комментария).
Вероятно, вам следует возвращать не лямбда-выражение, а указатель на функцию или std::function .
Помимо вопросов эффективности (см. Ниже), объявление API должно указывать вам, как его использовать. Лямбда — это временная функция — нечто, предназначенное для того, чтобы иметь смысл там, где вы его используете (в локальном контексте).
Если вы напишете это:
std::function<double( double )> makeFunction(double ratio);
Ваш код будет намного более гибким (не зависит от C 14), гораздо лучше определенным (вы можете посмотреть на подпись и узнать, что она делает) и рассчитанным на будущее (легко понять цель API, что упрощает последующее расширение без искажения клиентского кода).
Если вы напишете это:
auto makeFunction(double ratio);
Вы должны определить его встроенным (я думаю).
Для реализации, подобной ответу Майксеймура, вам нужно добавить дополнительную лямбду, чтобы избежать ограничения компилятора. Чтение реализации с лямбдой, возвращающей лямбду, только для того, чтобы выяснить, что возвращает API, является большим запретом.
Во-первых, потому что это неочевидно и неясно.
Во-вторых, это наложит на клиентский код API, который либо нуждается в комментарии / пояснительной записке, чтобы иметь смысл, либо заставляет клиентов читать его реализацию, чтобы знать, как его использовать.
Если вы не можете гарантировать, что это одноразовый код, вы:
-
увеличьте соотношение WTF / SLOC в вашем коде (назначение кода неясно, если вам нужно прочитать реализацию, чтобы выяснить, что она возвращает)
-
есть что-то неочевидное, что вам придется поддерживать для всего проекта. Это называется cruft и именно это делает отстойный устаревший код отстойным устаревшим кодом).
Если вам нужно вернуть функтор, вам лучше вернуть a std::function
; возвращаемый тип практически кричит «возвращает функтор».
Если это слишком неэффективно для ваших нужд, вы можете оптимизировать / конкретизировать его позже) — например, вернув struct X { double operator()(double); }
экземпляр.
Примечание о возможности гарантировать, что это не производственный код:
Я видел много случаев, когда:
-
Я смотрел на ужасный код трехлетней давности, где люди сказали мне, что изначально это был прототип, но мы показали приложение клиентам, они попросили его «как есть», и теперь клиенты зависят от него, но у нас нет бюджета времени на его очистку.
-
Я отправил уродливый код прототипа кому-то другому (например, «мы могли бы сделать что-то, что следует этому алгоритму») и позже увидел его в качестве решения в системе управления версиями.
-
Люди писали хаки поверх хаков, потому что код уже был взломан, и еще один взлом не имеет значения (на самом деле это очень распространенное оправдание).
Ответ №3:
Вы можете вернуть std::function<double( double )>
. Ее вызов может потребовать дополнительных затрат на вызов функции, которые в большинстве случаев незначительны.
Комментарии:
1. Это был бы хороший ответ, если бы вы также описали, каковы последствия (в основном для эффективности).
2. @Angew У вас есть какие-либо указания на информацию об этом? Мне нужно освежить мой C 11 🙂
3. @JoachimIsaksson самым большим является то, что
std::function
используется метод, называемый «стиранием типа», и это приводит к косвенному обращению к каждому вызову (часто реализуемому как виртуальная функция).4. @JoachimIsaksson Что говорит Simple.
5. 1; Я добавил ответ, который в основном поддерживает это (слишком длинный для комментария).