funciones - lambda examples c++
DeclaraciĆ³n directa de lambdas en C++ (3)
Estrictamente hablando no puedes
Citando de referencia cpp
La expresión lambda es una expresión de prvalue cuyo valor es (hasta C ++ 17) cuyo objeto de resultado es (desde C ++ 17) un objeto temporal sin nombre de tipo de clase no agregada no agregada sin unión única, conocido como tipo de cierre, que se declara (a los efectos de ADL) en el ámbito de bloque más pequeño, alcance de clase o ámbito de espacio de nombres que contiene la expresión lambda
Entonces, el lambda es un objeto temporal sin nombre . Puede vincular el lambda a un objeto l-value (por ejemplo, std::function
) y mediante reglas regulares sobre la declaración de variables puede separar la declaración y la definición.
En C ++, es posible separar la declaración y definición de funciones. Por ejemplo, es bastante normal declarar una función:
int Foo(int x);
en Foo.h
e implementarlo en Foo.cpp
. ¿Es posible hacer algo similar con lambdas? Por ejemplo, defina un
std::function<int(int)> bar;
en bar.h
e implementarlo en bar.cpp
como:
std::function<int(int)> bar = [](int n)
{
if (n >= 5)
return n;
return n*(n + 1);
};
Descargo de responsabilidad : Tengo experiencia con lambdas en C #, pero no los he usado mucho en C ++.
La declaración directa no es el término correcto porque las lambdas en C ++ son objetos, no funciones. El código:
std::function<int(int)> bar;
declara una variable y no está obligado a asignarla (ese tipo tiene un valor predeterminado de "puntero a ninguna función"). Puede compilar incluso llamadas a él ... por ejemplo, el código:
#include <functional>
#include <iostream>
int main(int argc, const char *argv[]) {
std::function<int(int)> bar;
std::cout << bar(21) << "/n";
return 0;
}
compilará limpiamente (pero, por supuesto, se comportará de manera loca en el tiempo de ejecución).
Dicho esto, puede asignar una lambda a una variable std::function
compatible y agregar, por ejemplo:
bar = [](int x){ return x*2; };
justo antes de la llamada dará como resultado un programa que compila bien y genera como salida 42.
Algunas cosas no obvias que pueden ser sorprendentes sobre lambdas en C ++ (si conoce otros idiomas que tienen este concepto) son que
Cada lambda
[..](...){...}
tiene un tipo incompatible diferente, incluso si la firma es absolutamente idéntica. Por ejemplo, usted no puede declarar un parámetro de tipo lambda porque la única forma sería usar algo comodecltype([] ...)
pero entonces no habría forma de llamar a la función como cualquier otra forma[]...
en un el sitio de llamada sería incompatible. Esto se soluciona constd::function
por lo que si tiene que pasar lambdas o almacenarlos en contenedores, debe usarstd::function
.Lambdas puede capturar locales por valor (pero son
const
menos que declares el lambdamutable
) o por referencia (pero garantizar la vida del objeto al que se hace referencia no será más corta que la vida del lambda depende del programador). C ++ no tiene un recolector de basura y esto es algo necesario para resolver correctamente el problema del "funar hacia arriba" (puede solucionar problemas mediante la captura de punteros inteligentes, pero debe prestar atención a los bucles de referencia para evitar fugas).A diferencia de otros idiomas, las lambdas se pueden copiar y cuando las copias, estás tomando una instantánea de sus variables internas de valor por captura. Esto puede ser muy sorprendente para el estado mutable y creo que la razón por la cual los valores de by-valor capturados son
const
por defecto.
Una forma de racionalizar y recordar muchos de los detalles sobre lambdas es ese código como:
std::function<int(int)> timesK(int k) {
return [k](int x){ return x*k; };
}
es básicamente como
std::function<int(int)> timesK(int k) {
struct __Lambda6502 {
int k;
__Lambda6502(int k) : k(k) {}
int operator()(int x) {
return x * k;
}
};
return __Lambda6502(k);
}
con una sutil diferencia que incluso las referencias de captura lambda pueden copiarse (normalmente las clases que contienen referencias como miembros no pueden).
No se puede separar la declaración y la definición de lambdas , ni reenviarlo declararlo. Su tipo es un tipo de cierre sin nombre que se declara con la expresión lambda. Pero podría hacer eso con los objetos std::function , que están diseñados para poder almacenar cualquier destino invocable, incluidas las expresiones lambda.
Como muestra el código de muestra que ha estado usando std::function
, solo tenga en cuenta que para este caso, bar
una variable global y necesita usar extern
en el archivo de encabezado para convertirlo en una declaración (no en una definición).
// bar.h
extern std::function<int(int)> bar; // declaration
y
// bar.cpp
std::function<int(int)> bar = [](int n) // definition
{
if (n >= 5) return n;
return n*(n + 1);
};
Note nuevamente que esta no es una declaración y definición separada de lambda; Es solo una declaración y definición separada de una bar
variables globales con tipo std::function<int(int)>
, que se inicializa a partir de una expresión lambda.