Expresiones de lambda en C ++

Expresiones de lambda en C ++

Por qué la expresión de lambda?

Considere la siguiente declaración:

int myint = 52;

Aquí, Myint es un identificador, un Lvalue. 52 es un literal, un praleo. Hoy, es posible codificar una función especialmente y ponerla en la posición de 52. Tal función se llama expresión de lambda. Considere también el siguiente programa corto:

#incluir
usando el espacio de nombres STD;
int fn (int par)

int respuesta = par + 3;
Respuesta de devolución;

int main ()

fn (5);
regresar 0;

Hoy, es posible codificar una función especialmente y ponerla en la posición del argumento de 5, de la llamada de función, fn (5). Tal función se llama expresión de lambda. La expresión de lambda (función) en esa posición es un praleo.

Cualquier literal, excepto la cadena, literal es un praleo. La expresión de Lambda es un diseño de función especial que encajaría como literal en el código. Es una función anónima (sin nombre). Este artículo explica la nueva expresión primaria de C ++, llamada expresión de lambda. El conocimiento básico en C ++ es un requisito para comprender este artículo.

Contenido del artículo

  • Ilustración de la expresión de lambda
  • Partes de la expresión de lambda
  • Capturas
  • Esquema de función de devolución de llamada clásica con expresión de lambda
  • El tipo de retorno
  • Cierre
  • Conclusión

Ilustración de la expresión de lambda

En el siguiente programa, una función, que es una expresión de lambda, se asigna a una variable:

#incluir
usando el espacio de nombres STD;
auto fn = [] (int param)

int respuesta = param + 3;
Respuesta de devolución;
;
int main ()

Variab automático = fn (2);
cout << variab << '\n';
regresar 0;

La salida es:

5

Fuera de la función main (), existe la variable, FN. Su tipo es automático. Auto en esta situación significa que el tipo real, como int o float, está determinado por el operando correcto del operador de asignación (=). A la derecha del operador de asignación hay una expresión lambda. Una expresión de lambda es una función sin el tipo de retorno anterior. Tenga en cuenta el uso y la posición de los soportes cuadrados, []. La función devuelve 5, un int, que determinará el tipo para FN.

En la función Main (), existe la declaración:

Variab automático = fn (2);

Esto significa, fn fuera main (), termina como el identificador para una función. Sus parámetros implícitos son los de la expresión de lambda. El tipo de variBIAB es automático.

Tenga en cuenta que la expresión de lambda termina con un punto y coma, al igual que la definición de clase o estructura, termina con un punto y coma.

En el siguiente programa, una función, que es una expresión de lambda que devuelve el valor de 5, es un argumento a otra función:

#incluir
usando el espacio de nombres STD;
void otherfn (int no1, int (*ptr) (int))

int no2 = (*ptr) (2);
cout << no1 << " << no2 << '\n';

int main ()

OTROFN (4, [] (int Param)

int respuesta = param + 3;
Respuesta de devolución;
);
regresar 0;

La salida es:

4 5

Hay dos funciones aquí, la expresión de Lambda y la función Otrofn (). La expresión de lambda es el segundo argumento de la otrafn (), llamada en main (). Tenga en cuenta que la función lambda (expresión) no termina con un punto y coma en esta llamada porque, aquí, es un argumento (no una función independiente).

El parámetro de la función Lambda en la definición de la función OTROFN () es un puntero a una función. El puntero tiene el nombre, PTR. El nombre, PTR, se usa en la definición OTROFN () para llamar a la función Lambda.

La declaración,

int no2 = (*ptr) (2);

En la definición de OTROFN (), llama a la función Lambda con un argumento de 2. El valor de retorno de la llamada, "(*ptr) (2)" de la función lambda, se asigna a NO2.

El programa anterior también muestra cómo la función Lambda se puede usar en el esquema de función de devolución de llamada C ++.

Partes de la expresión de lambda

Las partes de una función lambda típica son las siguientes:

[] ()
  • [] es la cláusula de captura. Puede tener artículos.
  • () es para la lista de parámetros.
  • es para el cuerpo de funciones. Si la función está sola, entonces debería terminar con un punto y coma.

Capturas

La definición de función Lambda se puede asignar a una variable o usarse como argumento a una llamada de función diferente. La definición de dicha llamada de función debe tener como parámetro, un puntero a una función, correspondiente a la definición de la función lambda.

La definición de la función lambda es diferente de la definición de función normal. Se puede asignar a una variable en el alcance global; Esta función asignada a variable también se puede codificar dentro de otra función. Cuando se asigna a una variable de alcance global, su cuerpo puede ver otras variables en el alcance global. Cuando se asigna a una variable dentro de una definición de función normal, su cuerpo puede ver otras variables en el alcance de la función solo con la ayuda de la cláusula de captura, [].

La cláusula de captura [], también conocida como Lambda-Introducer, permite enviar variables desde el alcance circundante (función) en el cuerpo de la función de la expresión de Lambda. Se dice que el cuerpo de la función de la expresión de Lambda captura la variable cuando recibe el objeto. Sin la cláusula de captura [], no se puede enviar una variable desde el alcance circundante al cuerpo de la expresión de lambda. El siguiente programa ilustra esto, con el alcance de la función Main (), como el alcance circundante:

#incluir
usando el espacio de nombres STD;
int main ()

int id = 5;
Auto fn = [id] ()

cout << id << '\n';
;
fn ();
regresar 0;

La salida es 5. Sin el nombre, ID, dentro [], la expresión de Lambda no habría visto la ID variable del alcance de la función principal ().

Capturando por referencia

El uso de ejemplo anterior de la cláusula de captura está capturando por valor (ver detalles a continuación). Al capturar por referencia, la ubicación (almacenamiento) de la variable, e.gramo., ID arriba, del alcance circundante, está disponible dentro del cuerpo de la función Lambda. Por lo tanto, cambiar el valor de la variable dentro del cuerpo de la función lambda cambiará el valor de esa misma variable en el alcance circundante. Cada variable repetida en la cláusula de captura está precedida por los amperandos (&) para lograr esto. El siguiente programa ilustra esto:

#incluir
usando el espacio de nombres STD;
int main ()

int id = 5; flotante ft = 2.3; char ch = 'a';
Auto fn = [& id, & ft, & ch] ()

id = 6; ft = 3.4; ch = 'b';
;
fn ();
cout << id << ", " << ft << ", " << ch << '\n';
regresar 0;

La salida es:

6, 3.4, B

Confirmar que los nombres de variables dentro del cuerpo de la función de la expresión de Lambda son para las mismas variables fuera de la expresión de Lambda.

Capturar por valor

Al capturar por valor, una copia de la ubicación de la variable, del alcance circundante, se pone a disposición dentro del cuerpo de la función lambda. Aunque la variable dentro del cuerpo de la función lambda es una copia, su valor no se puede cambiar dentro del cuerpo a partir de ahora. Para lograr la captura por valor, cada variable repetida en la cláusula de captura no está precedida por nada. El siguiente programa ilustra esto:

#incluir
usando el espacio de nombres STD;
int main ()

int id = 5; flotante ft = 2.3; char ch = 'a';
auto fn = [id, ft, ch] ()

// id = 6; ft = 3.4; ch = 'b';
cout << id << ", " << ft << ", " << ch << '\n';
;
fn ();
id = 6; ft = 3.4; ch = 'b';
cout << id << ", " << ft << ", " << ch << '\n';
regresar 0;

La salida es:

5, 2.3, A
6, 3.4, B

Si se elimina el indicador de comentarios, el programa no se compilará. El compilador emitirá un mensaje de error de que las variables dentro de la definición del cuerpo de la función de la expresión de lambda no se pueden cambiar. Aunque las variables no se pueden cambiar dentro de la función lambda, se pueden cambiar fuera de la función lambda, como muestra la salida del programa anterior.

Capturas de mezcla

La captura por referencia y la captura por valor se puede mezclar, como muestra el siguiente programa:

#incluir
usando el espacio de nombres STD;
int main ()

int id = 5; flotante ft = 2.3; char ch = 'a'; bool bl = verdadero;
Auto fn = [id, ft, & ch, & bl] ()

ch = 'b'; bl = falso;
cout << id << ", " <<
pie << ", " << ch <<
"," << bl << '\n';
;
fn ();
regresar 0;

La salida es:

5, 2.3, B, 0

Cuando todos capturados son por referencia:

Si todas las variables que se capturan se capturan por referencia, entonces solo una y será suficiente en la cláusula de captura. El siguiente programa ilustra esto:

#incluir
usando el espacio de nombres STD;
int main ()

int id = 5; flotante ft = 2.3; char ch = 'a'; bool bl = verdadero;
Auto fn = [&] ()

id = 6; ft = 3.4; ch = 'b'; bl = falso;
;
fn ();
cout << id << ", " <<
pie << ", " << ch <<
"," << bl << '\n';
regresar 0;

La salida es:

6, 3.4, b, 0

Si algunas variables deben ser capturadas por referencia y otras por valor, entonces una y representará todas las referencias, y el resto no será precedido por nada, como muestra el siguiente programa:

#incluir
usando el espacio de nombres STD;
int main ()

int id = 5; flotante ft = 2.3; char ch = 'a'; bool bl = verdadero;
Auto fn = [&, id, ft] ()

ch = 'b'; bl = falso;
cout << id << ", " <<
pie << ", " << ch <<
"," << bl << '\n';
;
fn ();
regresar 0;

La salida es:

5, 2.3, B, 0

Tenga en cuenta que y solo (yo.mi., y no seguido por un identificador) tiene que ser el primer personaje en la cláusula de captura.

Cuando todos capturados son por valor:

Si todas las variables que se capturan deben ser capturadas por valor, entonces solo una = será suficiente en la cláusula de captura. El siguiente programa ilustra esto:

#incluir
usando el espacio de nombres STD;
int main ()

int id = 5; flotante ft = 2.3; char ch = 'a'; bool bl = verdadero;
Auto fn = [=] ()

cout << id << ", " <<
pie << ", " << ch <<
"," << bl << '\n';
;
fn ();
regresar 0;

La salida es:

5, 2.3, A, 1

Nota: = es de solo lectura, a partir de ahora.

Si algunas variables deben ser capturadas por valor y otras por referencia, entonces uno = representará todas las variables copiadas de solo lectura, y el resto tendrá cada uno y, como muestra el siguiente programa:

#incluir
usando el espacio de nombres STD;
int main ()

int id = 5; flotante ft = 2.3; char ch = 'a'; bool bl = verdadero;
Auto fn = [=, & ch, & bl] ()

ch = 'b'; bl = falso;
cout << id << ", " << ft <<
"," << ch << ", " <<
licenciado en Derecho << '\n';
;
fn ();
regresar 0;

La salida es:

5, 2.3, B, 0

Tenga en cuenta que = solo tiene que ser el primer personaje en la cláusula de captura.

Esquema de función de devolución de llamada clásica con expresión de lambda

El siguiente programa muestra cómo se puede hacer un esquema de función de devolución de llamada clásica con la expresión de Lambda:

#incluir
usando el espacio de nombres STD;
char *salida;
auto cba = [] (char out [])

salida = out;
;
void principalfunc (char entrada [], void (*pt) (char []))

(*pt) (entrada);
cout<<"for principal function"<<'\n';

nulo fn ()

cout<<"Now"<<'\n';

int main ()

char entrada [] = "para la función de devolución de llamada";
principalfunc (entrada, CBA);
fn ();
cout<regresar 0;

La salida es:

Para la función principal
Ahora
Para la función de devolución de llamada

Recuerde que cuando una definición de expresión de Lambda se asigna a una variable en el alcance global, su cuerpo de función puede ver variables globales sin emplear la cláusula de captura.

El tipo de retorno

El tipo de retorno de una expresión lambda es automática, lo que significa que el compilador determina el tipo de retorno de la expresión de retorno (si está presente). Si el programador realmente quiere indicar el tipo de retorno, lo hará como en el siguiente programa:

#incluir
usando el espacio de nombres STD;
auto fn = [] (int param) -> int

int respuesta = param + 3;
Respuesta de devolución;
;
int main ()

Variab automático = fn (2);
cout << variab << '\n';
regresar 0;

La salida es 5. Después de la lista de parámetros, se escribe el operador de flecha. Esto es seguido por el tipo de retorno (int en este caso).

Cierre

Considere el siguiente segmento de código:

struct CLA

int id = 5;
char ch = 'a';
obj1, obj2;

Aquí, Cla es el nombre de la clase Struct. Obj1 y OBJ2 son dos objetos que se instanciarán desde la clase Struct. La expresión de Lambda es similar en la implementación. La definición de la función lambda es un tipo de clase. Cuando la función lambda se llama (invocada), un objeto se instancia desde su definición. Este objeto se llama cierre. Es el cierre el que hace el trabajo que hace la lambda.

Sin embargo, la codificación de la expresión de lambda como la estructura anterior tendrá OBJ1 y OBJ2 reemplazado por los argumentos de los parámetros correspondientes. El siguiente programa ilustra esto:

#incluir
usando el espacio de nombres STD;
auto fn = [] (int param1, int param2)

int respuesta = param1 + param2;
Respuesta de devolución;
(2, 3);
int main ()

auto var = fn;
cout << var << '\n';
regresar 0;

La salida es 5. Los argumentos son 2 y 3 entre paréntesis. Tenga en cuenta que la llamada de función de expresión de Lambda, FN, no toma ningún argumento ya que los argumentos ya se han codificado al final de la definición de la función Lambda.

Conclusión

La expresión de Lambda es una función anónima. Está en dos partes: clase y objeto. Su definición es un tipo de clase. Cuando se llama a la expresión, se forma un objeto a partir de la definición. Este objeto se llama cierre. Es el cierre el que hace el trabajo que hace la lambda. Para que la expresión de lambda reciba una variable de un alcance de la función exterior, necesita una cláusula de captura no vacía en su cuerpo de función.