Descubrí que los lvaluecierres lambda siempre se pueden pasar como rvalueparámetros de función.
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 un std::functionpara 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 de fn1cierre después de que la función regresó?

fn1se convierte implícitamente a unastd::functionenfoo(fn1). Esa función temporal es entonces un valor r.std::functionpueden deducirse los argumentos de plantilla de una lambda?". Su programa no intenta deducir los argumentos de la plantillastd::function, por lo que no hay problema con la conversión implícita.std::functiontiene un constructor no explícito que acepta cierres lambda, por lo que hay una conversión implícita. Pero en las circunstancias de la pregunta vinculada, la instanciación de plantilla destd::functionno se puede inferir del tipo lambda. (Por ejemplo,std::function<void()>se puede construir[](){return 5;}a pesar de que tiene un tipo de retorno no nulo.Respuestas:
La invocación
foorequiere una instanciastd::function<void()>que se una a una referencia de valor .std::function<void()>se puede construir a partir de cualquier objeto invocable que sea compatible con lavoid()firma.En primer lugar,
std::function<void()>se construye un objeto temporal[]{}. El constructor utilizado es el # 5 aquí , que copia el cierre en lastd::functioninstancia:Entonces, la
functioninstancia temporal está vinculada a la referencia rvalue.Igual que antes, porque se copió en una
std::functioninstancia. El cierre original no se ve afectado.fuente
Una lambda no es un
std::function. La referencia no se une directamente .El caso 1 funciona porque las lambdas son convertibles a
std::functions. Esto significa que unstd::functionmaterial temporal se materializa copiandofn1. 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 el motivo por el cual no
fn1se ve afectado por nada de lo que sucedefoo.fuente
fn1no tiene estado, ya que no captura nada.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 el lambda es invocable por los argumentos de esto
std::function, es implícitamente convertible a él a través del constructor de conversión de plantillas destd::function. El resultado de la conversión es un prvalue y, por lo tanto, puede vincularse con la referencia rvalue.fuente