Функция, возвращающая лямбда-выражение


88

Интересно, можно ли написать функцию, возвращающую лямбда-функцию на C ++ 11. Конечно, одна проблема - как объявить такую ​​функцию. У каждой лямбды есть тип, но этот тип нельзя выразить в C ++. Я не думаю, что это сработает:

auto retFun() -> decltype ([](int x) -> int)
{
    return [](int x) { return x; }
}

Ни это:

int(int) retFun();

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


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

3
ИМО, ваш первый вариант не будет работать, поскольку лямбда в decltypeтеле функции не такая же, как в теле функции, и поэтому имеет другой тип (даже если вы включили оператор возврата)
Мотти

1
Кстати, если лямбда-выражение имеет пустое предложение захвата, оно может быть неявно преобразовано в указатель на функцию.
GManNickG

@GMan: если вы не используете Visual C ++ 2010 или версию g ++, выпущенную более года назад (или около того). Неявное преобразование лямбда без захвата в указатель на функцию не было добавлено до марта 2010 года в N3092.
Джеймс МакНеллис

4
Лямбда-выражения вообще не могут появляться в неоцененных операндах. Так decltype([](){})или sizeof([]() {})плохо сформирован, где бы вы это не написали.
Йоханнес Шауб - лит

Ответы:


98

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

Этот пример возвращает целочисленную функцию идентификации:

std::function<int (int)> retFun() {
    return [](int x) { return x; };
}

6
Ах! Мне нравится StackOverflow. Мне потребовалось бы гораздо больше времени, чтобы изучить эту тему, а затем получить ответ на StackOverflow. Спасибо, Шон!
Bartosz Milewski

6
Это вызовет выделение памяти в конструкторе std::function.
Максим Егорушкин

2
@Maxim Yegorushkin std :: function имеет семантику перемещения, а также может использовать настраиваемые распределители, а в рабочем проекте C ++ 0x есть следующие примечания: «[Примечание: рекомендуется использовать реализации, чтобы избежать использования динамически выделяемой памяти для небольших вызываемых объектов, например , где цель f - это объект, содержащий только указатель или ссылку на объект и указатель на функцию-член. - конец примечания] "поэтому в основном вы не можете делать много предположений относительно того, какую стратегию распределения использует конкретная реализация, но вы должны иметь возможность в любом случае использовать свои собственные (объединенные) распределители.
snk_kid

1
@Maxim: Я ответил на вопрос: «Единственное ли решение - вручную создать объект-функцию и вернуть его?»
Шон

2
Просто имейте в виду, что std::functionиспользуется стирание типа, что может означать стоимость вызова виртуальной функции при вызове std::function. Что-то, о чем нужно знать, будет ли возвращенная функция использоваться в жестком внутреннем цикле или другом контексте, где имеет значение небольшая неэффективность.
Энтони Холл

29

Для этого простого примера вам не нужно std::function.

Из стандартного §5.1.2 / 6:

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

Поскольку ваша функция не имеет захвата, это означает, что лямбда может быть преобразована в указатель на функцию типа int (*)(int):

typedef int (*identity_t)(int); // works with gcc
identity_t retFun() { 
  return [](int x) { return x; };
}

Это мое понимание, поправьте меня, если я ошибаюсь.


1
Звучит правильно. К сожалению, это не работает с текущим компилятором, который я использую: VS 2010. Преобразование std :: function работает.
Bartosz Milewski

3
Да, окончательная формулировка этого правила пришла слишком поздно для VC2010.
Ben Voigt

Я добавил пример кода. Вот полная программа .
jfs

@JFSebastian - Какое время жизни лямбды в этом примере? Достаточно ли долго, чтобы пережить результат преобразования в указатель на функцию?
Flexo

1
@JFSebastian - Кажется, я не могу найти ответа на этот вопрос, поэтому я задал его как самостоятельный вопрос: stackoverflow.com/questions/8026170/…
Flexo

21

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

 auto retFun = []() {
     return [](int x) {return x;};
 };

2
Это верно только тогда, когда внешняя лямбда состоит только из оператора return. В противном случае вы должны указать тип возвращаемого значения.
Бартош Милевски

3
Это лучший ответ, поскольку он не требует полиморфизма std :: function во время выполнения и позволяет лямбда-выражению иметь непустой список захвата, однако я бы использовал const auto fun = ...
robson3.14

2
@BartoszMilewski не соответствует действительности с C ++ 14.
dzhioev

19

Хотя вопрос конкретно касается C ++ 11, для других, кто наткнется на это и имеет доступ к компилятору C ++ 14, C ++ 14 теперь позволяет выводить типы возвращаемого значения для обычных функций. Таким образом, пример в вопросе можно настроить так, чтобы он работал по желанию, просто отбросив предложение -> decltype... после списка параметров функции:

auto retFun()
{
    return [](int x) { return x; }
}

Учтите, однако, что это не сработает, если return <lambda>;в функции указано более одного . Это связано с тем, что ограничение на вывод типа возвращаемого значения состоит в том, что все операторы return должны возвращать выражения одного и того же типа, но каждому лямбда-объекту компилятор присваивает свой собственный уникальный тип, поэтому return <lambda>;каждое выражение будет иметь другой тип.


4
Почему упоминаются выведенные типы c ++ 14, но опускаются полиморфные лямбды? auto retFun() { return [](auto const& x) { return x; }; }
sehe

1

Вы должны написать так:

auto returnFunction = [](int x){
    return [&x](){
        return x;
    }();
};

чтобы получить доход как функцию, и используйте его как:

int val = returnFunction(someNumber);

0

Если у вас нет C ++ 11 и, например, вы запускаете код C ++ на микроконтроллерах. Вы можете вернуть указатель void, а затем выполнить приведение.

void* functionThatReturnsLambda()
{
    void(*someMethod)();

    // your lambda
    someMethod = []() {

        // code of lambda

    };

    return someMethod;
}


int main(int argc, char* argv[])
{

    void* myLambdaRaw = functionThatReturnsLambda();

    // cast it
    auto myLambda = (void(*)())myLambdaRaw;

    // execute lambda
    myLambda();
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.