2013-06-28 3 views
5

Я хочу, чтобы захватить «ссылку» на лямбда, и я подумал, что указатель функции будет делать трюк, как:Как создать ссылку на лямбда?

int (*factorial)(int) = [&](int x){ 
    return (x < 2) 
     ? 1 
     : x * factorial(x - 1); 
}; 

, но я получаю cannot convert from main::lambda<......> to int(_cdecl *)(int).

Каков правильный способ указать на лямбда?

+3

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

+0

Круто, ты прав. Полезно знать, спасибо. – sircodesalot

+0

Это почти дубликат http://stackoverflow.com/questions/2067988/recursive-lambda-functions-in-c0x – doctorlove

ответ

6

Поскольку лямбда не является апатридом, ее нельзя преобразовать в указатель функции. Вместо этого используйте std::function.

std::function<int(int)> factorial = [&](int x){ 
    return (x < 2) 
     ? 1 
     : x * factorial(x - 1); 
}; 
+1

У этого нетривиальные накладные расходы, как предупреждение, по сравнению с «сырыми» лямбдами. Каждый вызов примерно эквивалентен вызову метода 'virtual'. Это не так уж плохо, но по сравнению с работой, выполняемой в вызове, имеет большое значение. – Yakk

+0

@Yakk Да, вы платите стоимость стирания типа 'std :: function'; Я просто искал этот ответ из нескольких недель назад, чтобы связаться с :) – Praetorian

+0

Что это значит «не без гражданства»? Кроме того, @Yakk, каковы накладные расходы? – sircodesalot

5

Это будет ближе всего к тому, что у вас есть уже:

std::function<int (int)> factorial = [&](int x){ 
    return (x < 2) 
     ? 1 
     : x * factorial(x - 1); 
}; 

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

+1

'auto' не работает, см. Связанный вопрос. –

+0

@JesseХорошо: хорошая точка - спасибо. –

5

У вас уже есть хорошие ответы. Следующее - это просто любопытство, но я бы не предложил вам его использовать.

Как сказал другие, лямбда factorial пытается захватить себя и, следовательно, не является апатридом. Поэтому он не конвертируется в указатель функции.

Лямбда не нужно захватить глобальные или static объекты, так что если вы сделаете factorial глобальный или static переменную, то вам не нужно, чтобы захватить его, и это работает отлично (GCC 4.7.2)

#include <iostream> 

    typedef int (*function)(int); 

    int main() { 
     static function factorial = [](int x){ 
      return (x < 2) ? 1 : x * factorial(x - 1); 
     }; 
     std::cout << factorial(5) << '\n'; 
    } 

вы также можете создать фабрику так:

#include <iostream> 

    typedef int (*function)(int); 

    function make_factorial() { 
     static function factorial = [](int x){ 
      return (x < 2) ? 1 : x * factorial(x - 1); 
     }; 
     return factorial; 
    } 

    int main() { 
     auto factorial = make_factorial(); 
     std::cout << factorial(5) << '\n'; 
    } 

Если вы хотите, чтобы запутать еще больше :-) затем устранить typedef:

// This is a function returning a pointer to a function taking an int and returning an int. 
    int (*(make_factorial)())(int) { 
     static int (*factorial)(int) = [](int x){ 
      return (x < 2) ? 1 : x * factorial(x - 1); 
     }; 
     return factorial; 
    }