2011-01-18 13 views
61

Me pregunto si es posible escribir una función que devuelva una función lambda en C++ 11. Por supuesto, un problema es cómo declarar tal función. Cada lambda tiene un tipo, pero ese tipo no se puede expresar en C++. No creo que esto funcionaría:Función que devuelve una expresión lambda

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

Tampoco esto:

int(int) retFun(); 

No estoy al tanto de las conversiones automáticas de lambdas a, digamos, punteros a funciones, o algo así. ¿Es la única solución que elabora a mano un objeto de función y lo devuelve?

+0

Para agregar lo que ya se ha dicho, las funciones lambda sin estado son convertibles a punteros a función. –

+2

IMO su primera opción no funcionará porque la lambda en el 'decltype' no es la misma que en el cuerpo de la función y por lo tanto tiene un tipo diferente (incluso si incluyó la declaración de devolución) – Motti

+1

Por cierto, si un lambda tiene una cláusula de captura vacía, puede convertirse implícitamente en un puntero a función. – GManNickG

Respuesta

70

que no es necesario un objeto función artesanal, sólo tiene que utilizar std::function, a la que las funciones lambda son convertibles:

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

¡Duh! Me encanta StackOverflow. Me hubiera llevado mucho más tiempo investigar el tema y luego obtener la respuesta en StackOverflow. Gracias Sean! –

+3

Eso va a causar una asignación de memoria en el constructor de 'std :: function'. –

+2

@Maxim Yegorushkin std :: function tiene semántica de movimiento además de que puede usar asignadores personalizados y el borrador de trabajo C++ 0x tiene estas notas: "[Nota: se recomienda a las implementaciones que eviten el uso de memoria asignada dinámicamente para objetos pequeños invocables, ejemplo, donde el objetivo de f es un objeto que contiene solo un puntero o referencia a un objeto y un puntero a la función miembro. -end note] "así que básicamente no se pueden hacer muchas suposiciones sobre qué estrategia de asignación está usando una implementación en particular, pero debería capaz de usar sus propios asignadores (agrupados) de todos modos. –

27

Para este ejemplo sencillo, que no es necesario std::function.

De norma §5.1.2/6:

El tipo de cierre para un lambda-expresión sin lambda-captura tiene una función de conversión const no virtual no explícita pública de puntero para funcionar teniendo el mismo parámetro y tipos de retorno que el operador de llamada de función del tipo de cierre. El valor devuelto por esta función de conversión será la dirección de una función que, cuando se invoca, tiene el mismo efecto que invocar al operador de llamada de función del tipo de cierre.

Debido a que su función no tiene una captura, significa que el lambda se puede convertir en un puntero a función del tipo de int (*)(int):

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

Ese es mi entendimiento, me corrija si estoy incorrecto.

+1

Esto suena bien. Desafortunadamente, no funciona con el compilador actual que estoy usando: VS 2010. std :: function conversion funciona. –

+3

Sí, la redacción final de esta regla llegó demasiado tarde para VC2010. –

+0

He agregado un ejemplo de código. Aquí hay un [programa completo] (https://gist.github.com/1342212). – jfs

12

Puede devolver la función lambda desde otra función lambda, ya que no debe especificar explícitamente el tipo de retorno de la función lambda. Sólo tiene que escribir algo así en el ámbito global:

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

Eso solo es cierto cuando el lambda externo consiste solo en la declaración de devolución. De lo contrario, debe especificar el tipo de devolución. –

+1

Esta es la mejor respuesta, ya que no requiere el polimorfismo de tiempo de ejecución de std :: function y permite que lambda tenga una lista de captura no vacía, sin embargo, usaría ** const ** auto fun = ... –

10

Aunque la pregunta se refiere específicamente acerca de C++ 11, por el bien de otros que tropezar con esto y tener acceso a un compilador de C++ 14, C++ 14 ahora permite tipos de retorno deducidos para funciones ordinarias. Así que el ejemplo de la pregunta puede ser ajustado sólo para trabajar como deseada simplemente dejar caer la cláusula -> decltype ... después de la lista de parámetros de función:

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

Nótese, sin embargo, que esto no va a funcionar si más de un return <lambda>; aparece en la función. Esto se debe a que una restricción en la deducción del tipo de retorno es que todas las declaraciones de retorno deben devolver expresiones del mismo tipo, pero cada objeto lambda recibe su propio tipo único por el compilador, por lo que las expresiones return <lambda>; tendrán un tipo diferente.

+2

¿Por qué mencionar? ¿C++ 14 tipos deducidos pero omiten lambdas polimórficos? 'auto retFun() {return [] (auto const & x) {return x; }; } ' – sehe

Cuestiones relacionadas