Как вернуть лямбда-функцию?

#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; Я добавил ответ, который в основном поддерживает это (слишком длинный для комментария).