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?

mutablepalabra 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
autovariable en el inicializador de la misma. el tipo de la variable automática aún no se conoce cuando se procesa el inicializador.sumotra 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::functiontambié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
sumintento 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 desumen la última línea.operator()así que no hay nada que ganar reenviandosumconstsobrecarga en caso de que el objeto de función provisto tenga unconstoperador que no sea de llamada. Y usa SFINAE y calculanoexceptpara ambos. Además, ya no es necesaria la función de fabricante en C ++ 17.auto sumcopias ... 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::functionun 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::functionusos 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::functioncon un puntero de función (de núcleos solo funcionará con una función normal y lambdas sin estado). Por cierto,fib_nonrdebería aceptarfixpoint<int,int>, si utilizastd::functionsu 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 recpalabra 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_typey tener un operador()con argumentos comofp_type. Esto estará cercastd::functionpero permitirá un argumento de auto referencia.structtambién agregaría un nivel adicional de indirección. El ejemplo funciona y el elenco cumple con los estándares, no sé para qué-1fue.-1no sabía quién se lo daba, pero creo que es porquereinterpret_castdebería usarse como último recurso.castsupuestamente 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, lostructque propone es un functor, que utiliza un objeto lambda.std::functiony 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