Soy nuevo en C ++ 11. Estoy escribiendo la siguiente función recursiva lambda, pero no se compila.
sum.cpp
#include <iostream>
#include <functional>
auto term = [](int a)->int {
return a*a;
};
auto next = [](int a)->int {
return ++a;
};
auto sum = [term,next,&sum](int a, int b)mutable ->int {
if(a>b)
return 0;
else
return term(a) + sum(next(a),b);
};
int main(){
std::cout<<sum(1,10)<<std::endl;
return 0;
}
error de compilación:
vimal @ linux-718q: ~ / Study / 09C ++ / c ++ 0x / lambda> g ++ -std = c ++ 0x sum.cpp
sum.cpp: en la función lambda: sum.cpp: 18: 36: error: ' ((<lambda(int, int)>*)this)-><lambda(int, int)>::sum
' no se puede usar como una función
versión gcc
gcc versión 4.5.0 20091231 (experimental) (GCC)
Pero si cambio la declaración de la sum()
siguiente manera, funciona:
std::function<int(int,int)> sum = [term,next,&sum](int a, int b)->int {
if(a>b)
return 0;
else
return term(a) + sum(next(a),b);
};
¿Podría alguien arrojar luz sobre esto?
mutable
palabra clave allí?std::function<int(int,int)> sum = [&](int a, int b) {
Respuestas:
Piense en la diferencia entre la versión automática y la versión de tipo completamente especificada. La palabra clave automática infiere su tipo de lo que sea que se haya inicializado, pero lo que está inicializando necesita saber cuál es su tipo (en este caso, el cierre lambda necesita saber los tipos que está capturando). Algo de un problema de huevo y gallina.
Por otro lado, el tipo de un objeto de función completamente especificado no necesita "saber" nada sobre lo que se le está asignando, por lo que el cierre de la lambda también puede estar completamente informado sobre los tipos que captura.
Considere esta ligera modificación de su código y puede tener más sentido:
Obviamente, esto no funcionaría con auto . Las funciones lambda recursivas funcionan perfectamente bien (al menos lo hacen en MSVC, donde tengo experiencia con ellas), es solo que no son realmente compatibles con la inferencia de tipos.
fuente
auto
variable en el inicializador de la misma. el tipo de la variable automática aún no se conoce cuando se procesa el inicializador.sum
otra cosastd::function<int(int, int)>
o la especificación de C ++ simplemente no se molestó en inferirla?El truco consiste en introducir la implementación lambda en sí misma como parámetro , no mediante captura.
Todos los problemas en informática pueden resolverse mediante otro nivel de indirección . Primero encontré este truco fácil en http://pedromelendez.com/blog/2015/07/16/recursive-lambdas-in-c14/
Que no requiere C ++ 14, mientras que la cuestión está en C ++ 11, pero tal vez más interesante.
Ir a través de
std::function
también es posible pero puede resultar en un código más lento. Pero no siempre. Eche un vistazo a las respuestas a std :: function vs templateEsto no es solo una peculiaridad sobre C ++, sino que se asigna directamente a las matemáticas del cálculo lambda. De Wikipedia :
fuente
function<>
. No puedo ver por qué alguien lo preferiría. Editar: aparentemente es más rápido.error: use of ‘[...]’ before deduction of ‘auto’
- necesitaba especificar explícitamente el tipo de retorno (por otro lado, no necesitaba mutable).Con C ++ 14, ahora es bastante fácil hacer un lambda recursivo eficiente sin tener que incurrir en la sobrecarga adicional de
std::function
, en solo unas pocas líneas de código (con una pequeña edición del original para evitar que el usuario tome una copia accidental ):con lo que tu
sum
intento original se convierte en:En C ++ 17, con CTAD, podemos agregar una guía de deducción:
Lo que evita la necesidad de la función auxiliar. Solo podemos escribir
y_combinator{[](auto self, ...){...}}
directamente.En C ++ 20, con CTAD para agregados, la guía de deducción no será necesaria.
fuente
std::forward<decltype(sum)>(sum)
lugar desum
en la última línea.operator()
así que no hay nada que ganar reenviandosum
const
sobrecarga en caso de que el objeto de función provisto tenga unconst
operador que no sea de llamada. Y usa SFINAE y calculanoexcept
para ambos. Además, ya no es necesaria la función de fabricante en C ++ 17.auto sum
copias ... pero copia areference_wrapper
, que es lo mismo que tomar una referencia. Hacerlo una vez en la implementación significa que ninguno de los usos se copiará accidentalmente.Tengo otra solución, pero solo trabajo con lambdas sin estado:
El truco aquí es que lambdas puede acceder a variables estáticas y puede convertir las sin estado en puntero de función.
Puedes usarlo con lambdas estándar:
Su trabajo en GCC 4.7
fuente
Usted puede hacer una función lambda llama a sí mismo de forma recursiva. Lo único que debe hacer es hacer referencia a él a través de un contenedor de funciones para que el compilador sepa su tipo de retorno y argumento (no puede capturar una variable, la lambda misma, que aún no se ha definido) .
Tenga mucho cuidado de no salirse del alcance del envoltorio f.
fuente
Para hacer que lambda sea recursiva sin usar clases y funciones externas (como
std::function
un combinador de punto fijo), se puede usar la siguiente construcción en C ++ 14 ( ejemplo en vivo ):huellas dactilares:
Tenga en cuenta que el tipo de resultado de lambda debe especificarse explícitamente.
fuente
Ejecuté un punto de referencia comparando una función recursiva frente a una función lambda recursiva utilizando el
std::function<>
método de captura. Con las optimizaciones completas habilitadas en la versión 4.1 de clang, la versión lambda se ejecutó significativamente más lenta.Produce resultados:
(Nota: también confirmó con una versión que tomó las entradas de cin, para eliminar la evaluación del tiempo de compilación)
Clang también produce una advertencia de compilación:
Lo cual es esperado y seguro, pero debe tenerse en cuenta.
Es genial tener una solución en nuestros cinturones de herramientas, pero creo que el lenguaje necesitará una mejor manera de manejar este caso si el rendimiento es comparable a los métodos actuales.
Nota:
Como señaló un comentarista, parece que la última versión de VC ++ ha encontrado una manera de optimizar esto hasta el punto de un rendimiento igual. Tal vez no necesitemos una mejor manera de manejar esto, después de todo (excepto el azúcar sintáctico).
Además, como algunas otras publicaciones de SO han descrito en las últimas semanas, el rendimiento en
std::function<>
sí mismo puede ser la causa de la desaceleración frente a la función de llamada directamente, al menos cuando la captura lambda es demasiado grande para caber en algunosstd::function
usos de espacio optimizados para bibliotecas para pequeños functores (¿Supongo que te gustan las diversas optimizaciones de cadenas cortas?).fuente
Esta es una implementación un poco más simple del operador de punto fijo que lo hace un poco más obvio exactamente lo que está sucediendo.
fuente
std::function
con un puntero de función (de núcleos solo funcionará con una función normal y lambdas sin estado). Por cierto,fib_nonr
debería aceptarfixpoint<int,int>
, si utilizastd::function
su requiere crear una nueva copia*this
.Aquí hay una versión refinada de la solución Y-combinator basada en una propuesta por @Barry.
Para usar esto, uno podría hacer lo siguiente
Es similar a la
let rec
palabra clave en OCaml, aunque no es lo mismo.fuente
C ++ 14: Aquí hay un conjunto genérico de lambdas anónimo recursivo sin estado / sin captura que genera todos los números del 1, 20
Si entiendo correctamente, esto está usando la solución del combinador Y
Y aquí está la versión suma (n, m)
fuente
Aquí está la respuesta final para el OP. De todos modos, Visual Studio 2010 no admite la captura de variables globales. Y no es necesario capturarlos porque la variable global es accesible globalmente mediante define. La siguiente respuesta utiliza la variable local en su lugar.
fuente
Estás intentando capturar una variable (suma) que estás definiendo. Eso no puede ser bueno.
No creo que las lambdas C ++ 0x verdaderamente recursivas sean posibles. Sin embargo, deberías poder capturar otras lambdas.
fuente
Esta respuesta es inferior a la de Yankees, pero aún así, aquí va:
fuente
reinterpret_cast
. Probablemente la mejor manera en su caso es crear alguna estructura que reemplacedp_type
. Debe tener campofp_type
, puede construirse a partir defp_type
y tener un operador()
con argumentos comofp_type
. Esto estará cercastd::function
pero permitirá un argumento de auto referencia.struct
también agregaría un nivel adicional de indirección. El ejemplo funciona y el elenco cumple con los estándares, no sé para qué-1
fue.-1
no sabía quién se lo daba, pero creo que es porquereinterpret_cast
debería usarse como último recurso.cast
supuestamente garantiza que funcione por el C ++ 11 estándar. Usar unstruct
, en mis ojos, podría vencer el uso de un objeto lambda. Después de todo, lostruct
que propone es un functor, que utiliza un objeto lambda.std::function
y tendrá algo parecido a lo que tenía en mente. Esto probablemente tendrá un rendimiento similar a su solución.Necesitas un combinador de punto fijo. Mira esto .
o mira el siguiente código:
fuente