captura por parte de los miembros de la clase de valor

5 minutos de lectura

Avatar de usuario de Lorenzo Pistone
lorenzo pistón

¿Hay alguna forma, al escribir una función lambda dentro de una función miembro, de capturar campos de la clase adjunta por valor? El cajón de sastre predeterminado = no funciona porque cuando hago referencia a la variable dentro de la lambda, obtengo una desreferencia del puntero capturado, además de nombrar explícitamente la variable en la lista de captura, porque recibo dos errores de compilación: capture of non-variable <name>y ‘this’ was not captured for this lambda function

  • Respuesta corta: No. Respuesta más larga: haga referencias locales de función a los miembros de datos, luego capture esas referencias locales por valor, pero este esfuerzo manual es inevitable.

    – ildjarn

    17/10/2012 a las 21:55


  • @ildjarn, ¿cómo es que una limitación tan tonta?

    – Lorenzo Pistón

    17/10/2012 a las 22:04

  • Eso parece realmente inconsistente. No puedo pensar por qué eso sería razonable.

    – bames53

    17/10/2012 a las 22:06

  • @Lorenzo: No sé la razón. Tal vez alguien del comité se detenga con una respuesta autorizada, llamando a @HowardHinnant…

    – ildjarn

    17/10/2012 a las 22:15

Avatar de usuario de James McNellis
james mcnellis

No, los miembros de datos no se pueden capturar por valor. Una lambda puede capturar solo dos tipos de cosas:

  1. el this puntero, y
  2. variables locales no estáticas (es decir, variables con duración de almacenamiento automático).

Como ha señalado ildjarn en los comentarios, puede crear una variable local con una copia del valor del miembro de datos y capturar esa variable local por valor.

Yo discutiría eso si Si se permitía la captura por valor explícito de un miembro de datos, podría resultar confuso, ya que el comportamiento de la captura explícita diferiría del de la captura implícita. Por ejemplo, dado un miembro de datos accesible de tipo int nombrada msería extraño que lo siguiente produjera resultados diferentes:

[=] () mutable { m = 1; } // we modify this->m
[=m]() mutable { m = 1; } // we modify the copy of m that was captured

  • Bueno, eso es una tontería. No puedo imaginar por qué pensaron que era una buena idea.

    – Pato mugido

    17/10/2012 a las 22:32

  • @MooingDuck: consistencia. Digamos que llamamos a una función miembro no estática desde el cuerpo de la primera lambda: f();. ¿Espera que se llame a esa función miembro? *this o en una copia de *this?

    –James McNellis

    17/10/2012 a las 22:38

  • @MooingDuck: la captura lambda siempre captura variables del ámbito local. Las variables miembro no están en el ámbito de configuración regional, por lo que no puede tocarlas. Además, se accede realmente al miembro como this->m excepto que no tienes que escribir this->. Dicho esto, discutimos las capturas de miembros para lambda el lunes y no hay una buena solución, por lo que sé.

    – Dietmar Kühl

    17/10/2012 a las 22:57

  • @DietmarKühl: Correcto. estaba diciendo eso si estaba permitido, terminaríamos con la inconsistencia señalada en el ejemplo. Aparentemente, el “si” implícito no estaba claro; Mis disculpas a todos los que pueden haber estado confundidos. [Aside: I hope you all are having fun in Portland :-)]

    –James McNellis

    17/10/2012 a las 22:59


  • La capacidad de captura de expresiones arbitrarias de C++14 debería resolver este problema, al permitir la captura por valor y darle un nombre diferente al mismo tiempo, evitando así la inconsistencia.

    – Ben Voigt

    4 oct 2013 a las 16:26

Sí, simplemente escribe el [<new name>=<your class field>] construir. Por ejemplo:

class MyClass {
    int a;
    void foo() {
        auto my_lambda = [a_by_val=a] {
            // do something with a_by_val
        }

        my_lambda();
    }
}

  • Los inicializadores de captura son una característica de C+14, la pregunta está etiquetada como C++11.

    – Fluir en

    14 de mayo de 2019 a las 7:28

C++ es un lenguaje de velocidad, y una de las cosas más importantes en el diseño del lenguaje es el rendimiento. Si los diseñadores estándar quieren implementar una forma de capturar todas las variables de una clase por valor, piensen en una clase realmente grande y díganme, ¿quieren capturarlas todas por valor? incluso para capturar todas las variables declaradas en una función (incluyendo this) hay 2 vías:

  • por valor: las variables que se definen en una función, se dejan en la pila, por lo que no pueden ser de varios megabytes, sino clases que usan un montón como vector todavía puede ser enorme. ¡Así que ten cuidado al usar esto!

  • por referencia: que en realidad guarda el puntero de la pila.

Entonces, verá que no hay una forma eficiente de capturar todas las variables de una clase con valor, ¡pero puede hacer una copia local de esa clase en su función y luego capturarla por referencia!

  • realmente no. No quiero capturarlos a todos. Solo quiero capturar uno, por valor, con cualquier sintaxis en la lista de captura.

    – Lorenzo Pistón

    17/10/2012 a las 22:23

  • Si bien el código rápido se puede escribir en C ++, creo que no es del todo exacto clasificar el lenguaje en torno a la velocidad. Se trata de mucho más que velocidad.

    – John Dibling

    18 de octubre de 2012 a las 0:28

  • tu argumento es discutible, digamos que tengo BigObject foo; entonces está perfectamente bien capturar todo el objeto por valor, pero dentro de un método de BigObject no puede capturar un solo miembro, no importa lo barato que sea copiar ese solo miembro

    – 463035818_no_es_un_número

    25 de marzo de 2019 a las 12:51

¿Ha sido útil esta solución?