¿Qué es una expresión lambda en C ++ 11?


1488

¿Qué es una expresión lambda en C ++ 11? ¿Cuándo usaría uno? ¿Qué clase de problema resuelven que no era posible antes de su introducción?

Algunos ejemplos y casos de uso serían útiles.


14
He visto un caso en el que el lambda fue muy útil: un colega mío estaba haciendo código que tiene millones de iteraciones para resolver un problema de optimización del espacio. ¡El algoritmo era mucho más rápido cuando se usaba una lambda que una función adecuada! El compilador es Visual C ++ 2013.
sergiol

Respuestas:


1491

El problema

C ++ incluye funciones genéricas útiles como std::for_eachy std::transform, que pueden ser muy útiles. Desafortunadamente, también pueden ser bastante engorrosos de usar, especialmente si el functor que desea aplicar es único para la función particular.

#include <algorithm>
#include <vector>

namespace {
  struct f {
    void operator()(int) {
      // do something
    }
  };
}

void func(std::vector<int>& v) {
  f f;
  std::for_each(v.begin(), v.end(), f);
}

Si solo usa funa vez y en ese lugar específico, parece excesivo escribir toda una clase solo para hacer algo trivial y único.

En C ++ 03 puede que tenga la tentación de escribir algo como lo siguiente, para mantener el functor local:

void func2(std::vector<int>& v) {
  struct {
    void operator()(int) {
       // do something
    }
  } f;
  std::for_each(v.begin(), v.end(), f);
}

sin embargo, esto no está permitido, fno se puede pasar a una función de plantilla en C ++ 03.

La nueva solución

C ++ 11 presenta lambdas que le permiten escribir un functor anónimo en línea para reemplazar el struct f. Para pequeños ejemplos simples, esto puede ser más limpio de leer (mantiene todo en un solo lugar) y potencialmente más simple de mantener, por ejemplo en la forma más simple:

void func3(std::vector<int>& v) {
  std::for_each(v.begin(), v.end(), [](int) { /* do something here*/ });
}

Las funciones de Lambda son simplemente azúcar sintáctica para functores anónimos.

Tipos de retorno

En casos simples, el tipo de retorno de la lambda se deduce por usted, por ejemplo:

void func4(std::vector<double>& v) {
  std::transform(v.begin(), v.end(), v.begin(),
                 [](double d) { return d < 0.00001 ? 0 : d; }
                 );
}

sin embargo, cuando comience a escribir lambdas más complejas, encontrará rápidamente casos en los que el compilador no puede deducir el tipo de retorno, por ejemplo:

void func4(std::vector<double>& v) {
    std::transform(v.begin(), v.end(), v.begin(),
        [](double d) {
            if (d < 0.0001) {
                return 0;
            } else {
                return d;
            }
        });
}

Para resolver esto, puede especificar explícitamente un tipo de retorno para una función lambda, utilizando -> T:

void func4(std::vector<double>& v) {
    std::transform(v.begin(), v.end(), v.begin(),
        [](double d) -> double {
            if (d < 0.0001) {
                return 0;
            } else {
                return d;
            }
        });
}

Variables de "captura"

Hasta ahora no hemos usado nada más que lo que se pasó al lambda dentro de él, pero también podemos usar otras variables, dentro del lambda. Si desea acceder a otras variables, puede usar la cláusula de captura (la []de la expresión), que hasta ahora no se ha utilizado en estos ejemplos, por ejemplo:

void func5(std::vector<double>& v, const double& epsilon) {
    std::transform(v.begin(), v.end(), v.begin(),
        [epsilon](double d) -> double {
            if (d < epsilon) {
                return 0;
            } else {
                return d;
            }
        });
}

Puede capturar tanto por referencia como por valor, que puede especificar usando &y =respectivamente:

  • [&epsilon] captura por referencia
  • [&] captura todas las variables utilizadas en el lambda por referencia
  • [=] captura todas las variables utilizadas en lambda por valor
  • [&, epsilon] captura variables como con [&], pero epsilon por valor
  • [=, &epsilon] captura variables como con [=], pero epsilon por referencia

El generado operator()es constpor defecto, con la implicación de que las capturas serán constcuando accedas a ellas por defecto. Esto tiene el efecto de que cada llamada con la misma entrada produciría el mismo resultado, sin embargo, puede marcar el lambdamutable para solicitar que lo operator()que se produce no sea const.


99
@Yakk has quedado atrapado. Las lambdas sin captura tienen una conversión implícita a punteros de tipo de función. la función de conversión es constsiempre ...
Johannes Schaub - litb

2
@ JohannesSchaub-litb oh astuto, y sucede cuando invocas (), se pasa como un lambda de argumento cero, pero debido a () constque no coincide con el lambda, busca una conversión de tipo que lo permita, que incluye la conversión implícita -to-function-pointer, y luego llama a eso! ¡Furtivo!
Yakk - Adam Nevraumont 01 de

2
Interesante: originalmente pensé que las lambdas eran funciones anónimas en lugar de functores, y estaba confundido sobre cómo funcionaban las capturas.
user253751

50
Si desea usar lambdas como variables en su programa, puede usar: std::function<double(int, bool)> f = [](int a, bool b) -> double { ... }; Pero generalmente, dejamos que el compilador deduzca el tipo: auto f = [](int a, bool b) -> double { ... }; (y no olvide #include <functional>)
Evert Heylen

11
Supongo que no todos entienden por qué return d < 0.00001 ? 0 : d;se garantiza que devolverá el doble, cuando uno de los operandos es una constante entera (es debido a una regla de promoción implícita del operador?: Donde el segundo y tercer operando se equilibran entre sí a través de la aritmética habitual conversiones sin importar cuál sea elegido). Cambiar a 0.0 : dtal vez haría que el ejemplo sea más fácil de entender.
Lundin

831

¿Qué es una función lambda?

El concepto C ++ de una función lambda se origina en el cálculo lambda y la programación funcional. Una lambda es una función sin nombre que es útil (en la programación real, no en la teoría) para fragmentos cortos de código que son imposibles de reutilizar y no vale la pena nombrar.

En C ++, una función lambda se define así

[]() { } // barebone lambda

o en todo su esplendor

[]() mutable -> T { } // T is the return type, still lacking throw()

[]es la lista de captura, ()la lista de argumentos y {}el cuerpo de la función.

La lista de captura

La lista de captura define qué desde el exterior del lambda debería estar disponible dentro del cuerpo de la función y cómo. Puede ser:

  1. un valor: [x]
  2. una referencia [& x]
  3. cualquier variable actualmente en alcance por referencia [&]
  4. igual que 3, pero por valor [=]

Puede mezclar cualquiera de los anteriores en una lista separada por comas [x, &y].

La lista de argumentos

La lista de argumentos es la misma que en cualquier otra función de C ++.

El cuerpo de la función

El código que se ejecutará cuando se llame realmente al lambda.

Tipo de devolución deducción

Si una lambda tiene una sola declaración de retorno, el tipo de retorno puede omitirse y tiene el tipo implícito de decltype(return_statement).

Mudable

Si una lambda está marcada como mutable (por ejemplo []() mutable { }), se le permite mutar los valores que han sido capturados por valor.

Casos de uso

La biblioteca definida por el estándar ISO se beneficia en gran medida de las lambdas y aumenta la usabilidad varias barras, ya que ahora los usuarios no tienen que saturar su código con pequeños functors en un alcance accesible.

C ++ 14

En C ++ 14 lambdas se han ampliado con varias propuestas.

Capturas de Lambda inicializadas

Ahora se puede inicializar un elemento de la lista de captura con =. Esto permite cambiar el nombre de las variables y capturar moviendo. Un ejemplo tomado del estándar:

int x = 4;
auto y = [&r = x, x = x+1]()->int {
            r += 2;
            return x+2;
         }();  // Updates ::x to 6, and initializes y to 7.

y uno tomado de Wikipedia que muestra cómo capturar con std::move:

auto ptr = std::make_unique<int>(10); // See below for std::make_unique
auto lambda = [ptr = std::move(ptr)] {return *ptr;};

Lambdas Genéricas

Lambdas ahora puede ser genérico ( autosería equivalente a Taquí si Tfuera un argumento de plantilla de tipo en algún lugar del alcance circundante):

auto lambda = [](auto x, auto y) {return x + y;};

Deducción de tipo de devolución mejorada

C ++ 14 permite tipos de retorno deducidos para cada función y no lo restringe a funciones del formulario return expression;. Esto también se extiende a las lambdas.


2
En su ejemplo para las capturas lambda inicializadas anteriores, ¿por qué termina la función lamba con () ;? Esto aparece como [] () {} (); en lugar de [](){};. Además, ¿no debería ser el valor de x 5?
Ramakrishnan Kannan

77
@RamakrishnanKannan: 1) los () están ahí para llamar al lambda justo después de definirlo y darle su valor de retorno. La variable y es un número entero, no la lambda. 2) No, x = 5 es local para el lambda (una captura por valor que simplemente tiene el mismo nombre que la variable de alcance externo x), y luego se devuelve x + 2 = 5 + 2. La reasignación de la variable externa x ocurre a través de la referencia r:, r = &x; r += 2;pero esto sucede con el valor original de 4.
The Vee

168

Las expresiones lambda se usan típicamente para encapsular algoritmos para que puedan pasarse a otra función. Sin embargo, es posible ejecutar un lambda inmediatamente después de la definición :

[&](){ ...your code... }(); // immediately executed lambda expression

es funcionalmente equivalente a

{ ...your code... } // simple code block

Esto hace que las expresiones lambda sean una herramienta poderosa para refactorizar funciones complejas . Comienza envolviendo una sección de código en una función lambda como se muestra arriba. El proceso de parametrización explícita se puede realizar gradualmente con pruebas intermedias después de cada paso. Una vez que tiene el bloque de código completamente parametrizado (como lo demuestra la eliminación del &), puede mover el código a una ubicación externa y convertirlo en una función normal.

Del mismo modo, puede usar expresiones lambda para inicializar variables basadas en el resultado de un algoritmo ...

int a = []( int b ){ int r=1; while (b>0) r*=b--; return r; }(5); // 5!

Como una forma de particionar la lógica de su programa , incluso puede resultarle útil pasar una expresión lambda como argumento a otra expresión lambda ...

[&]( std::function<void()> algorithm ) // wrapper section
   {
   ...your wrapper code...
   algorithm();
   ...your wrapper code...
   }
([&]() // algorithm section
   {
   ...your algorithm code...
   });

Las expresiones Lambda también le permiten crear funciones anidadas con nombre , que pueden ser una forma conveniente de evitar la lógica duplicada. El uso de lambdas con nombre también tiende a ser un poco más fácil para la vista (en comparación con lambdas en línea anónimas) al pasar una función no trivial como parámetro a otra función. Nota: no olvide el punto y coma después del cierre de llaves.

auto algorithm = [&]( double x, double m, double b ) -> double
   {
   return m*x+b;
   };

int a=algorithm(1,2,3), b=algorithm(4,5,6);

Si la creación de perfiles posterior revela una sobrecarga de inicialización significativa para el objeto de función, puede optar por reescribir esto como una función normal.


11
¿Te has dado cuenta de que esta pregunta se hizo hace 1,5 años y que la última actividad fue hace casi 1 año? De todos modos, ¡estás aportando algunas ideas interesantes que no había visto antes!
Piotr99

77
¡Gracias por el consejo simultáneo de definir y ejecutar! Creo que vale la pena señalar que eso funciona como una contidion para las ifdeclaraciones: if ([i]{ for (char j : i) if (!isspace(j)) return false ; return true ; }()) // i is all whitespacesuponiendo que isea ​​unstd::string
Blacklight Shining

74
Así que la siguiente es una expresión legal: [](){}();.
nobar

8
Ugh! La (lambda: None)()sintaxis de Python es mucho más legible.
dan04

99
@nobar: tienes razón, escribí mal. Esto es legal (lo probé esta vez)main() {{{{((([](){{}}())));}}}}
Mark Lakata

38

Respuestas

P: ¿Qué es una expresión lambda en C ++ 11?

R: Bajo el capó, es el objeto de una clase autogenerada con sobrecarga operator () const . Dicho objeto se llama cierre y lo crea el compilador. Este concepto de 'cierre' está cerca del concepto de enlace de C ++ 11. Pero las lambdas generalmente generan un mejor código. Y las llamadas a través de cierres permiten una alineación completa.

P: ¿Cuándo usaría uno?

R: Para definir "lógica simple y pequeña" y pedirle al compilador que realice la generación de la pregunta anterior. Le da al compilador algunas expresiones que desea que estén dentro del operador (). El resto del compilador de cosas te generará.

P: ¿Qué clase de problema resuelven que no era posible antes de su introducción?

R: Es una especie de azúcar de sintaxis, como la sobrecarga de operadores en lugar de funciones para operaciones personalizadas de suma, resta ... ¡Pero guarda más líneas de código innecesario para ajustar 1-3 líneas de lógica real a algunas clases, etc.! Algunos ingenieros piensan que si el número de líneas es menor, entonces hay menos posibilidades de cometer errores (también lo creo)

Ejemplo de uso

auto x = [=](int arg1){printf("%i", arg1); };
void(*f)(int) = x;
f(1);
x(1);

Extras sobre lambdas, no cubiertos por la pregunta. Ignora esta sección si no te interesa

1. Valores capturados. Lo que puedes capturar

1.1. Puede hacer referencia a una variable con duración de almacenamiento estático en lambdas. Todos son capturados.

1.2. Puede usar lambda para capturar valores "por valor". En tal caso, los vars capturados se copiarán al objeto de función (cierre).

[captureVar1,captureVar2](int arg1){}

1.3. Puede capturar ser referencia. & - en este contexto, significa referencia, no punteros.

   [&captureVar1,&captureVar2](int arg1){}

1.4. Existe notación para capturar todos los valores no estáticos por valor o por referencia

  [=](int arg1){} // capture all not-static vars by value

  [&](int arg1){} // capture all not-static vars by reference

1.5. Existe notación para capturar todos los vars no estáticos por valor, o por referencia y especificar smth. más. Ejemplos: Capture todos los valores no estáticos por valor, sino por captura de referencia Param2

[=,&Param2](int arg1){} 

Capture todos los vars no estáticos por referencia, pero mediante captura de valor Param2

[&,Param2](int arg1){} 

2. Tipo de devolución deducción

2.1. El tipo de retorno Lambda se puede deducir si lambda es una expresión. O puede especificarlo explícitamente.

[=](int arg1)->trailing_return_type{return trailing_return_type();}

Si lambda tiene más de una expresión, el tipo de retorno debe especificarse mediante el tipo de retorno final. Además, se puede aplicar una sintaxis similar a las funciones automáticas y las funciones miembro

3. Valores capturados. Lo que no puedes capturar

3.1. Puede capturar solo variables locales, no variables de miembro del objeto.

4. Conversiones

4.1 !! Lambda no es un puntero de función y no es una función anónima, pero las lambdas sin captura se pueden convertir implícitamente en un puntero de función.

PD

  1. Puede encontrar más información sobre la gramática lambda en el Borrador de trabajo para el lenguaje de programación C ++ # 337, 2012-01-16, 5.1.2. Expresiones Lambda, p.88

  2. En C ++ 14 se ha agregado la característica adicional que se ha denominado como "captura inicial". Permite realizar declaraciones de miembros de datos de cierre arbitrariamente:

    auto toFloat = [](int value) { return float(value);};
    auto interpolate = [min = toFloat(0), max = toFloat(255)](int value)->float { return (value - min) / (max - min);};

Esto [&,=Param2](int arg1){}no parece ser una sintaxis válida. La forma correcta sería[&,Param2](int arg1){}
GetFree

Gracias. Primero intenté compilar este fragmento. Y parece extraño asimetría en modificadores permitidos en la lista de captura // g ++ -std = c ++ 11 main.cpp -o test_bin; ./test_bin #include <stdio.h> int main () {#if 1 {int param = 0; auto f = [=, & param] (int arg1) mutable {param = arg1;}; f (111); printf ("% i \ n", param); } #endif #if 0 {int param = 0; auto f = [&, = param] (int arg1) mutable {param = arg1;}; f (111); printf ("% i \ n", param); } #endif return 0; }
bruziuz

Parece que la nueva línea no se admite en el comentario. Luego abrí 5.1.2 Expresiones lambda, p.88, "Borrador de trabajo, estándar para el lenguaje de programación C ++", Número de aviso: # 337, 2012-01-16. Y examinó la sintaxis gramatical. Y tienes razón. No existe tal cosa como la captura a través de "= arg"
bruziuz

Muchas gracias, lo arreglé en la descripción y también adquirí nuevos conocimientos.
bruziuz

16

Una función lambda es una función anónima que crea en línea. Puede capturar variables como algunos han explicado (por ejemplo, http://www.stroustrup.com/C++11FAQ.html#lambda ) pero existen algunas limitaciones. Por ejemplo, si hay una interfaz de devolución de llamada como esta,

void apply(void (*f)(int)) {
    f(10);
    f(20);
    f(30);
}

puede escribir una función en el acto para usarla como la que se pasa para aplicar a continuación:

int col=0;
void output() {
    apply([](int data) {
        cout << data << ((++col % 10) ? ' ' : '\n');
    });
}

Pero no puedes hacer esto:

void output(int n) {
    int col=0;
    apply([&col,n](int data) {
        cout << data << ((++col % 10) ? ' ' : '\n');
    });
}

debido a limitaciones en el estándar C ++ 11. Si desea utilizar capturas, debe confiar en la biblioteca y

#include <functional> 

(o alguna otra biblioteca STL como el algoritmo para obtenerlo indirectamente) y luego trabajar con std :: function en lugar de pasar funciones normales como parámetros como este:

#include <functional>
void apply(std::function<void(int)> f) {
    f(10);
    f(20);
    f(30);
}
void output(int width) {
    int col;
    apply([width,&col](int data) {
        cout << data << ((++col % width) ? ' ' : '\n');
    });
}

1
la razón es que una lambda solo puede convertirse en un puntero de función si no tiene captura si applyfuera una plantilla que aceptara un functor, funcionaría
sp2danny

1
Pero el problema es que si apply es una interfaz existente, es posible que no pueda darse el lujo de poder declararla de manera diferente a una función antigua. El estándar podría haber sido diseñado para permitir que se genere una nueva instancia de una función antigua simple cada vez que se ejecuta dicha expresión lambda, con referencias codificadas generadas a las variables capturadas. Parece que se genera una función lambda en tiempo de compilación. También hay otras consecuencias. por ejemplo, si declara una variable estática, incluso si vuelve a evaluar la expresión lambda, no obtendrá una nueva variable estática.
Ted

1
El puntero de función a menudo está destinado a guardarse, y una captura lambdas puede quedar fuera de alcance. que sólo lambdas de captura-menos se convierten en función de triples fue por diseño
sp2danny

1
Todavía debe prestar atención a las variables de pila que se desasignan por la misma razón de cualquier manera. Vea blogs.msdn.com/b/nativeconcurrency/archive/2012/01/29/…. El ejemplo que escribí con output y apply está escrito de modo que si en su lugar se permitieran y utilizaran punteros de función, también funcionarían. La columna permanece asignada hasta que todas las llamadas de función desde aplicar hayan finalizado. ¿Cómo reescribiría este código para que funcione con la interfaz de aplicación existente? ¿Terminarías usando variables globales o estáticas, o alguna transformación más oscura del código?
Ted

1
o tal vez simplemente quiere decir que las expresiones lambda son valores y, por lo tanto, temporales, sin embargo, el código permanece constante (singleton / static) para que pueda llamarse en el futuro. En ese caso, tal vez la función debería permanecer asignada mientras sus capturas asignadas por pila permanezcan asignadas. Por supuesto, podría ser complicado desenrollarlo si, por ejemplo, muchas variaciones de la función se asignan en un bucle.
Ted

12

Una de las mejores explicaciones lambda expressiones la del autor de C ++ Bjarne Stroustrup en su ***The C++ Programming Language***capítulo 11 del libro ( ISBN-13: 978-0321563842 ):

What is a lambda expression?

Una expresión lambda , a veces también denominada función lambda o (estrictamente hablando incorrectamente, pero coloquialmente) como lambda , es una notación simplificada para definir y usar un objeto de función anónimo . En lugar de definir una clase con nombre con un operador (), luego hacer un objeto de esa clase y finalmente invocarlo, podemos usar una taquigrafía.

When would I use one?

Esto es particularmente útil cuando queremos pasar una operación como argumento a un algoritmo. En el contexto de las interfaces gráficas de usuario (y en otros lugares), tales operaciones a menudo se denominan devoluciones de llamada .

What class of problem do they solve that wasn't possible prior to their introduction?

Aquí supongo que cada acción realizada con la expresión lambda se puede resolver sin ellas, pero con mucho más código y una complejidad mucho mayor. Expresión Lambda: esta es la forma de optimización de su código y una forma de hacerlo más atractivo. Tan triste por Stroustup:

formas efectivas de optimizar

Some examples

a través de la expresión lambda

void print_modulo(const vector<int>& v, ostream& os, int m) // output v[i] to os if v[i]%m==0
{
    for_each(begin(v),end(v),
        [&os,m](int x) { 
           if (x%m==0) os << x << '\n';
         });
}

o a través de la función

class Modulo_print {
         ostream& os; // members to hold the capture list int m;
     public:
         Modulo_print(ostream& s, int mm) :os(s), m(mm) {} 
         void operator()(int x) const
           { 
             if (x%m==0) os << x << '\n'; 
           }
};

o incluso

void print_modulo(const vector<int>& v, ostream& os, int m) 
     // output v[i] to os if v[i]%m==0
{
    class Modulo_print {
        ostream& os; // members to hold the capture list
        int m; 
        public:
           Modulo_print (ostream& s, int mm) :os(s), m(mm) {}
           void operator()(int x) const
           { 
               if (x%m==0) os << x << '\n';
           }
     };
     for_each(begin(v),end(v),Modulo_print{os,m}); 
}

si lo necesita puede nombrar lambda expressioncomo a continuación:

void print_modulo(const vector<int>& v, ostream& os, int m)
    // output v[i] to os if v[i]%m==0
{
      auto Modulo_print = [&os,m] (int x) { if (x%m==0) os << x << '\n'; };
      for_each(begin(v),end(v),Modulo_print);
 }

O asuma otra muestra simple

void TestFunctions::simpleLambda() {
    bool sensitive = true;
    std::vector<int> v = std::vector<int>({1,33,3,4,5,6,7});

    sort(v.begin(),v.end(),
         [sensitive](int x, int y) {
             printf("\n%i\n",  x < y);
             return sensitive ? x < y : abs(x) < abs(y);
         });


    printf("sorted");
    for_each(v.begin(), v.end(),
             [](int x) {
                 printf("x - %i;", x);
             }
             );
}

generará el próximo

0 0

1

0 0

1

0 0

1

0 0

1

0 0

1

0 clasificado x - 1; x - 3; x - 4; x - 5; x - 6; x - 7; x - 33;

[]- esta es una lista de captura o lambda introducer: si lambdasno requiere acceso a su entorno local, podemos usarla.

Cita del libro:

El primer carácter de una expresión lambda es siempre [ . Un introductor lambda puede tomar varias formas:

[] : una lista de captura vacía. Esto implica que no se pueden usar nombres locales del contexto circundante en el cuerpo lambda. Para tales expresiones lambda, los datos se obtienen de argumentos o de variables no locales.

[&] : captura implícitamente por referencia. Se pueden usar todos los nombres locales. Se accede a todas las variables locales por referencia.

[=] : captura implícitamente por valor. Se pueden usar todos los nombres locales. Todos los nombres se refieren a copias de las variables locales tomadas en el punto de llamada de la expresión lambda.

[lista de captura]: captura explícita; la lista de captura es la lista de nombres de variables locales que se capturarán (es decir, se almacenarán en el objeto) por referencia o por valor. Las variables con nombres precedidos por & se capturan por referencia. Otras variables son capturadas por valor. Una lista de captura también puede contener esto y nombres seguidos de ... como elementos.

[&, capture-list] : captura implícitamente por referencia todas las variables locales con nombres no mencionados en la lista. La lista de captura puede contener esto. Los nombres listados no pueden estar precedidos por &. Las variables nombradas en la lista de captura se capturan por valor.

[=, lista de captura] : captura implícitamente por valor todas las variables locales con nombres no mencionados en la lista. La lista de captura no puede contener esto. Los nombres enumerados deben ir precedidos de &. Las variables nombradas en la lista de captura se capturan por referencia.

Tenga en cuenta que un nombre local precedido por & siempre se captura por referencia y un nombre local no precedido por & siempre se captura por valor. Solo la captura por referencia permite la modificación de variables en el entorno de llamada.

Additional

Lambda expression formato

ingrese la descripción de la imagen aquí

Referencias adicionales:


Buena explicación Usando el rango para bucles, puede evitar lambdas y acortar el códigofor (int x : v) { if (x % m == 0) os << x << '\n';}
Dietrich Baumgarten

2

Bueno, un uso práctico que descubrí es reducir el código de la placa de la caldera. Por ejemplo:

void process_z_vec(vector<int>& vec)
{
  auto print_2d = [](const vector<int>& board, int bsize)
  {
    for(int i = 0; i<bsize; i++)
    {
      for(int j=0; j<bsize; j++)
      {
        cout << board[bsize*i+j] << " ";
      }
      cout << "\n";
    }
  };
  // Do sth with the vec.
  print_2d(vec,x_size);
  // Do sth else with the vec.
  print_2d(vec,y_size);
  //... 
}

Sin lambda, es posible que deba hacer algo para diferentes bsizecasos. Por supuesto, podría crear una función, pero ¿qué sucede si desea limitar el uso dentro del alcance de la función de usuario del alma? la naturaleza de lambda cumple este requisito y lo uso para ese caso.


2

Las lambda en c ++ se tratan como "funciones disponibles en el camino". sí, está literalmente en movimiento, usted lo define; úsalo; y cuando el alcance de la función principal termina, la función lambda desaparece.

c ++ lo introdujo en c ++ 11 y todos comenzaron a usarlo como en todos los lugares posibles. el ejemplo y lo que es lambda se puede encontrar aquí https://en.cppreference.com/w/cpp/language/lambda

Describiré cuál no está allí, pero es esencial saberlo para cada programador de C ++

Lambda no está destinado a usarse en todas partes y todas las funciones no se pueden reemplazar con lambda. Tampoco es el más rápido en comparación con la función normal. porque tiene algunos gastos generales que deben ser manejados por lambda.

seguramente ayudará a reducir el número de líneas en algunos casos. se puede usar básicamente para la sección de código, que se llama en la misma función una o más veces y ese fragmento de código no se necesita en ningún otro lugar para que pueda crear una función independiente para él.

A continuación se muestra el ejemplo básico de lambda y lo que sucede en segundo plano.

Codigo de usuario:

int main()
{
  // Lambda & auto
  int member=10;
  auto endGame = [=](int a, int b){ return a+b+member;};

  endGame(4,5);

  return 0;

}

Cómo se expande la compilación:

int main()
{
  int member = 10;

  class __lambda_6_18
  {
    int member;
    public: 
    inline /*constexpr */ int operator()(int a, int b) const
    {
      return a + b + member;
    }

    public: __lambda_6_18(int _member)
    : member{_member}
    {}

  };

  __lambda_6_18 endGame = __lambda_6_18{member};
  endGame.operator()(4, 5);

  return 0;
}

como puede ver, qué tipo de sobrecarga agrega cuando lo usa. así que no es buena idea usarlos en todas partes. Se puede utilizar en lugares donde sean aplicables.


sí, está literalmente en movimiento, usted lo define; úsalo; y cuando el alcance de la función principal termina, la función lambda desaparece ... ¿qué pasa si la función devuelve el lambda a la persona que llama?
Nawaz

1
Tampoco es el más rápido en comparación con la función normal. porque tiene algunos gastos generales que deben ser manejados por lambda. ¿Alguna vez ha ejecutado algún punto de referencia para respaldar esta afirmación ? Por el contrario, las plantillas lambda + a menudo producen el código más rápido posible.
Nawaz

1

Un problema que resuelve: código más simple que lambda para una llamada en el constructor que usa una función de parámetro de salida para inicializar un miembro constante

Puede inicializar un miembro constante de su clase, con una llamada a una función que establece su valor devolviendo su salida como un parámetro de salida.


Esto también se puede hacer con una función simple, que es incluso lo que dice la respuesta aceptada a la pregunta que ha vinculado.
SirGuy
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.