Expresiones lambda en C ++

Lambda Expressions C



¿Por qué Lambda Expression?

Considere la siguiente declaración:

En tmyInt= 52;

Aquí, myInt es un identificador, un lvalue. 52 es un valor literal. Hoy en día, es posible codificar una función especialmente y ponerla en la posición 52. Esta función se llama expresión lambda. Considere también el siguiente programa corto:







#incluir

utilizando espacio de nombreshoras;

En tfn(En tmediante)

{

En trespuesta=mediante+ 3;

regresorespuesta;

}


En tprincipal()

{

fn(5);



regreso 0;

}

Hoy en día, es posible codificar una función especialmente y ponerla en la posición del argumento de 5, de la llamada a la función, fn (5). Esta función se denomina expresión lambda. La expresión lambda (función) en esa posición es un prvalue.



Cualquier literal excepto el literal de cadena es un valor pr. La expresión 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 ++, denominada expresión 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 lambda

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





#incluir

utilizando espacio de nombreshoras;

autofn= [](En tparada)

{

En trespuesta=parada+ 3;

regresorespuesta;

};


En tprincipal()

{

autovariab=fn(2);

costo <<variab<< ' orte';


regreso 0;

}

La salida es:

5

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



En la función main (), está la declaración:

autovariab=fn(2);

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

Tenga en cuenta que la expresión 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 lambda que devuelve el valor 5, es un argumento para otra función:

#incluir

utilizando espacio de nombreshoras;

vacíootherfn(En tno1,En t (*ptr)(En t))

{

En tno2= (*ptr)(2);

costo <<no1<< ' ' <<no2<< ' orte';

}


En tprincipal()

{

otherfn(4,[](En tparada)

{

En trespuesta=parada+ 3;

regresorespuesta;

});


regreso 0;
}

La salida es:

4 5

Aquí hay dos funciones, la expresión lambda y la función otherfn (). La expresión lambda es el segundo argumento de otherfn (), llamado 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 otherfn () es un puntero a una función. El puntero tiene el nombre ptr. El nombre, ptr, se usa en la definición de otherfn () para llamar a la función lambda.

La declaración,

En tno2= (*ptr)(2);

En la definición otherfn (), 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 se puede utilizar la función lambda en el esquema de función de devolución de llamada de C ++.

Partes de la expresión Lambda

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

[] () {}
  • [] es la cláusula de captura. Puede tener elementos.
  • () es para la lista de parámetros.
  • {} es para el cuerpo de la función. Si la función es independiente, debe terminar con un punto y coma.

Capturas

La definición de la función lambda puede asignarse a una variable o usarse como argumento para una llamada de función diferente. La definición de dicha llamada a 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 la función normal. Puede asignarse a una variable en el ámbito global; esta función-asignada-a-variable también se puede codificar dentro de otra función. Cuando se asigna a una variable de ámbito global, su cuerpo puede ver otras variables en el ámbito 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 el introductor lambda, permite que las variables se envíen desde el ámbito circundante (función) al cuerpo de la función de la expresión lambda. Se dice que el cuerpo de la función de la expresión lambda captura la variable cuando recibe el objeto. Sin la cláusula de captura [], no se puede enviar una variable desde el ámbito circundante al cuerpo de la función de la expresión lambda. El siguiente programa ilustra esto, con el alcance de la función main (), como el alcance circundante:

#incluir

utilizando espacio de nombreshoras;

En tprincipal()

{

En tidentificación= 5;


autofn= [identificación]()

{

costo <<identificación<< ' orte';

};

fn();


regreso 0;

}

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

Capturar por referencia

El ejemplo anterior de uso de la cláusula de captura es la captura por valor (consulte los detalles a continuación). Al capturar por referencia, la ubicación (almacenamiento) de la variable, por ejemplo, id arriba, del alcance circundante, está disponible dentro del cuerpo de la función lambda. Entonces, cambiar el valor de la variable dentro del cuerpo de la función lambda cambiará el valor de esa misma variable en el ámbito circundante. Cada variable repetida en la cláusula de captura está precedida por el ampersand (&) para lograr esto. El siguiente programa ilustra esto:

#incluir

utilizando espacio de nombreshoras;

En tprincipal()

{

En tidentificación= 5; flotadorpie= 2.3; carbonizarsech= 'A';

autofn= [&identificación,&pie,&ch]()

{

identificación= 6;pie= 3.4;ch= 'B';

};

fn();

costo <<identificación<< ', ' <<pie<< ', ' <<ch<< ' orte';

regreso 0;

}

La salida es:

6, 3.4, B

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

Capturando por valor

Al capturar por valor, una copia de la ubicación de la variable, del alcance circundante, está disponible 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 de nada. El siguiente programa ilustra esto:

#incluir

utilizando espacio de nombreshoras;

En tprincipal()

{

En tidentificación= 5; flotadorpie= 2.3; carbonizarsech= 'A';

autofn= [id, ft, ch]()

{

// id = 6; ft = 3,4; ch = 'B';

costo <<identificación<< ', ' <<pie<< ', ' <<ch<< ' orte';

};

fn();

identificación= 6;pie= 3.4;ch= 'B';

costo <<identificación<< ', ' <<pie<< ', ' <<ch<< ' orte';

regreso 0;

}

La salida es:

5, 2.3, A

6, 3.4, B

Si se quita el indicador de comentario, el programa no se compilará. El compilador emitirá un mensaje de error que indica que las variables dentro de la definición del cuerpo de la función de la expresión 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 pueden combinar, como muestra el siguiente programa:

#incluir

utilizando espacio de nombreshoras;

En tprincipal()

{

En tidentificación= 5; flotadorpie= 2.3; carbonizarsech= 'A'; boollicenciado en Derecho= cierto;


autofn= [id, ft,&ch,&licenciado en Derecho]()

{

ch= 'B';licenciado en Derecho= falso;

costo <<identificación<< ', ' <<pie<< ', ' <<ch<< ', ' <<licenciado en Derecho<< ' orte';

};

fn();


regreso 0;

}

La salida es:

5, 2.3, B, 0

Cuando todos capturados, son por referencia:

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

#incluir

utilizando espacio de nombreshoras;

En tprincipal()

{

En tidentificación= 5; flotadorpie= 2.3; carbonizarsech= 'A'; boollicenciado en Derecho= cierto;


autofn= [&]()

{

identificación= 6;pie= 3.4;ch= 'B';licenciado en Derecho= falso;

};

fn();

costo <<identificación<< ', ' <<pie<< ', ' <<ch<< ', ' <<licenciado en Derecho<< ' orte';


regreso 0;

}

La salida es:

6, 3.4, B, 0

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

utilizando espacio de nombreshoras;

En tprincipal()

{

En tidentificación= 5; flotadorpie= 2.3; carbonizarsech= 'A'; boollicenciado en Derecho= cierto;


autofn= [&, id, ft]()

{

ch= 'B';licenciado en Derecho= falso;

costo <<identificación<< ', ' <<pie<< ', ' <<ch<< ', ' <<licenciado en Derecho<< ' orte';

};

fn();


regreso 0;

}

La salida es:

5, 2.3, B, 0

Tenga en cuenta que & solo (es decir, & no seguido de un identificador) tiene que ser el primer carácter en la cláusula de captura.

Cuando todos capturados, están por valor:

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

#incluir

utilizando espacio de nombreshoras;

En tprincipal()
{

En tidentificación= 5; flotadorpie= 2.3; carbonizarsech= 'A'; boollicenciado en Derecho= cierto;


autofn= [=]()

{

costo <<identificación<< ', ' <<pie<< ', ' <<ch<< ', ' <<licenciado en Derecho<< ' orte';

};

fn();


regreso 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 una = representará todas las variables copiadas de solo lectura, y el resto tendrá &, como muestra el siguiente programa:

#incluir

utilizando espacio de nombreshoras;

En tprincipal()

{

En tidentificación= 5; flotadorpie= 2.3; carbonizarsech= 'A'; boollicenciado en Derecho= cierto;


autofn= [=,&ch,&licenciado en Derecho]()

{

ch= 'B';licenciado en Derecho= falso;

costo <<identificación<< ', ' <<pie<< ', ' <<ch<< ', ' <<licenciado en Derecho<< ' orte';

};

fn();


regreso 0;

}

La salida es:

5, 2.3, B, 0

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

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

El siguiente programa muestra cómo se puede realizar un esquema de función de devolución de llamada clásico con la expresión lambda:

#incluir

utilizando espacio de nombreshoras;

carbonizarse *producción;


autocba= [](carbonizarsefuera[])

{

producción=fuera;

};



vacíoprincipalFunc(carbonizarseaporte[],vacío (*por)(carbonizarse[]))

{

(*por)(aporte);

costo<<'para función principal'<<' orte';

}


vacíofn()

{

costo<<'Ahora'<<' orte';

}


En tprincipal()

{

carbonizarseaporte[] = 'para la función de devolución de llamada';

principalFunc(entrada, cba);

fn();

costo<<producción<<' orte';



regreso 0;

}

La salida es:

para función principal

Ahora

para la función de devolución de llamada

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

El tipo de retorno final

El tipo de retorno de una expresión lambda es automático, lo que significa que el compilador determina el tipo de retorno a partir 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

utilizando espacio de nombreshoras;

autofn= [](En tparada) -> En t

{

En trespuesta=parada+ 3;

regresorespuesta;

};


En tprincipal()

{

autovariab=fn(2);

costo <<variab<< ' orte';


regreso 0;

}

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

Cierre

Considere el siguiente segmento de código:

estructuraCla

{

En tidentificación= 5;

carbonizarsech= 'a';

}obj1, obj2;

Aquí, Cla es el nombre de la clase de estructura. Obj1 y obj2 son dos objetos que serán instanciados desde la clase struct. La expresión lambda es similar en implementación. La definición de la función lambda es una especie de clase. Cuando se llama (invoca) a la función lambda, se crea una instancia de un objeto a partir de su definición. Este objeto se llama cierre. Es el cierre el que hace el trabajo que se espera que haga la lambda.

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

#incluir

utilizando espacio de nombreshoras;

autofn= [](En tparam1,En tparam2)

{

En trespuesta=param1+param2;

regresorespuesta;

} (2,3);


En tprincipal()

{

autodónde=fn;

costo <<dónde<< ' orte';


regreso 0;

}

El resultado es 5. Los argumentos son 2 y 3 entre paréntesis. Tenga en cuenta que la llamada a la función de expresión lambda, fn, no acepta 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 lambda es una función anónima. Tiene dos partes: clase y objeto. Su definición es una especie 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 se espera que haga la lambda.

Para que la expresión lambda reciba una variable de un ámbito de función externo, necesita una cláusula de captura no vacía en su cuerpo de función.