sintaxis programacion matematicas lambdas expresiones c++ c++11 lambda

c++ - programacion - ¿Cuándo es ''esto'' capturado en un lambda?



lambda sintaxis (6)

Tengo una función en una clase que define un lambda y lo almacena en una variable estática local:

class A { public: void call_print() { static auto const print_func = [this] { print(); }; print_func(); }; virtual void print() { std::cout << "A::print()/n"; } }; class B : public A { public: virtual void print() override { std::cout << "B::print()/n"; } };

También ejecuto la siguiente prueba:

int main() { A a; B b; a.call_print(); b.call_print(); }

( Muestra en vivo )

Lo que espero que se imprima es:

A::print() B::print()

Pero lo que realmente entiendo es:

A::print() A::print()

(La misma dirección del objeto también se imprime con cada uno)

Sospecho que esto se debe a this captura. Asumí que capturaría el valor de this cuando se llame, sin embargo, parece que se captura en el momento en que se define la lambda.

¿Podría alguien explicar la semántica de las capturas lambda? ¿Cuándo son realmente proporcionados a la función? ¿Es el mismo para todos los tipos de captura, o es un caso especial? Al eliminar la static soluciona el problema, sin embargo, en mi código de producción, en realidad estoy almacenando el lambda en un objeto ligeramente más pesado que representa una ranura en la que luego inserto en una señal.


De hecho, el valor de la captura se establece cuando se define la lambda, no cuando se llama. Debido a que está configurando una variable estática para la expresión que define la lambda, esto solo ocurre la primera vez que se llama a la función call_print (por las reglas que rigen las variables estáticas). Por lo tanto, todas call_print llamadas de call_print posteriores, de hecho, obtienen el mismo lambda, el que está configurado como &a .


Esta pregunta no es tanto sobre el comportamiento de las lambdas, sino sobre el comportamiento de las variables estáticas en las funciones.

La variable se crea la primera vez que el código fluye sobre ella, utilizando las variables disponibles en ese momento.

ejemplo:

#include <iostream> int foo(int v) { static int value = v; return v; }; int main() { std::cout << foo(10) << std::endl; std::cout << foo(11) << std::endl; }

esperado:

10 10

Porque es equivalente a:

foo::value = 10; std::cout << foo::value << std::endl; // foo::value = 11; (does not happen) std::cout << foo::value << std::endl;


Esto no tiene nada que ver con la semántica de la captura lambda. Es simplemente cómo funciona la static .

Una variable static de ámbito de función se inicializa exactamente una vez . Solo hay un objeto de este tipo en todo tu programa. Se inicializará la primera vez que se llame a la función (más específicamente, la primera vez que se ejecute static instrucción static ). Y, por lo tanto, la expresión utilizada para inicializar la variable static solo se invoca una vez.

Entonces, si una variable static de ámbito de función se inicializa con datos que se basan en uno de los parámetros de la función (como this ), solo obtendrá los parámetros de la primera invocación de esa función.

Tu código crea un solo lambda. No crea diferentes lambdas en cada invocación de la función.

El comportamiento que parece querer no es una variable static función local, sino un miembro objeto. Así que solo coloque un objeto std::function en la clase en sí, y call_print que call_print inicialice si está vacío.


Lambda se crea una instancia la primera vez que se llama a la función de A::call_print() . Desde la primera vez que lo llama en un objeto A , se captura el objeto de ese objeto. Si invierte el orden de invocación, verá un resultado diferente:

b.call_print(); a.call_print();

Salida:

B::print() B::print()

Tiene más que ver con la semántica de inicialización de objetos estáticos de función local que con la captura lambda.


Las variables locales estáticas se inicializan la primera vez que se ejecuta su declaración.

En este caso, usted:

  1. Cree un lambda, capturando & A como esto, que llame a A.print ();
  2. Asigna ese lambda a print_func
  3. Llama a eso lambda (a través de print_func )
  4. Llama a esa lambda de nuevo.

Los valores capturados siempre se capturan cuando se crea el lambda, que en este caso es durante la primera llamada a call_print .


No creo que la captura sea el problema aquí, sino la palabra clave static . Piensa en tu código como esto:

class A { public: void call_print() { static A* ptr = this; ptr->print(); }; virtual void print() { std::cout << "A::print()/n"; } }; class B : public A { public: virtual void print() override { std::cout << "B::print()/n"; } };

Esto es prácticamente lo mismo sin un lambda.

Si observa el código, queda bastante claro que su llamada a.call_print(); inicializa ptr con un puntero al objeto a que luego se usa más adelante.