c++ lambda closures c++14 rvalue-reference

c++ - Los valores de cierre de Lambda se pueden pasar como parámetros de referencia de valor



closures c++14 (3)

Descubrí que los cierres lvalue lambda siempre se pueden pasar como parámetros de la función rvalue .

Vea la siguiente demostración simple.

#include <iostream> #include <functional> using namespace std; void foo(std::function<void()>&& t) { } int main() { // Case 1: passing a `lvalue` closure auto fn1 = []{}; foo(fn1); // works // Case 2: passing a `lvalue` function object std::function<void()> fn2 = []{}; foo(fn2); // compile error return 0; }

El caso 2 es el comportamiento estándar (acabo de utilizar una std::function para fines de demostración, pero cualquier otro tipo se comportaría igual).

¿Cómo y por qué funciona el caso 1? ¿Cuál es el estado del cierre de fn1 después de que la función regresó?


¿Cómo y por qué funciona el caso 1?

Invocar foo requiere una instancia de std::function<void()> que se une a una referencia rvalue . std::function<void()> puede construirse a partir de cualquier objeto invocable que sea compatible con la firma void() .

En primer lugar, un objeto temporal std::function<void()> se construye a partir de []{} . El constructor utilizado es el # 5 here , que copia el cierre en la instancia std::function :

template< class F > function( F f );

Inicializa el objetivo con std::move(f) . Si f es un puntero nulo para funcionar o un puntero nulo para miembro, *this estará vacío después de la llamada.

Entonces, la instancia de function temporal está vinculada a la referencia rvalue.

¿Cuál es el estado del cierre de fn1 después de que la función regresó?

Igual que antes, porque se copió en una instancia std::function . El cierre original no se ve afectado.


¿Cuál es el estado del cierre de fn1 después de que la función regresó?

fn1 tiene estado, ya que no captura nada.

¿Cómo y por qué funciona el caso 1?

Funciona porque el argumento es de un tipo diferente al tipo al que se hace referencia rvalue. Debido a que tiene un tipo diferente, se consideran las conversiones implícitas. Dado que lambda es invocable para los argumentos de esta std::function , es convertible implícitamente a través del constructor de conversión de plantillas de std::function . El resultado de la conversión es un prvalue y, por lo tanto, puede vincularse con la referencia rvalue.


Una lambda no es una std::function . La referencia no se une directamente .

El caso 1 funciona porque las lambdas son convertibles a std::function s. Esto significa que una std::function temporal se materializa copiando fn1 . Dicho temporal puede vincularse a una referencia de valor r, por lo que el argumento coincide con el parámetro.

Y la copia también es la razón fn1 cual fn1 se ve afectado por nada de lo que sucede en foo .