funciones examples c++ c++11 lambda declaration definition

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 como decltype([] ...) 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 con std::function por lo que si tiene que pasar lambdas o almacenarlos en contenedores, debe usar std::function .

  • Lambdas puede capturar locales por valor (pero son const menos que declares el lambda mutable ) 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.