c++ - que - std:: bind pierde la referencia cuando se entrega como referencia rvalue
ref c# (2)
std::bind()
está diseñado para semántica de valor ( como R. Martinho Fernandes explica muy bien en su respuesta ), y crea copias internamente. Lo que necesitas / quieres es std::ref
:
callback(std::bind(test, std::ref(t)));
// ^^^^^^^^^^^
std::ref
devuelve un objeto std::reference_wrapper<>
que envuelve una referencia a su argumento original. De esta manera, el objeto reference_wrapper
alrededor de t
se copia, y no t
sí mismo.
Esto le permite elegir entre semántica de valor (asumida por defecto) y semántica de referencia (que requiere su intervención explícita).
Aquí hay un ejemplo en vivo .
Tengo el siguiente código:
#include <stdio.h>
#include <functional>
template <typename T>
auto callback(T&& func) ->decltype(func())
{
return func();
}
double test(double& value)
{
value=value+1.0;
return value;
}
int main(void)
{
double t=1.0;
printf("%f/n",t);
test(t);
printf("%f/n",t);
callback(std::bind(test,t));
printf("%f/n",t);
}
Y da salida
1.000000
2.000000
2.000000
Lo que implica que la función de callback
obtuvo una copia de t
lugar de una referencia a t
. Me pregunto qué sucedió, ya que para std::bind
debería ser un reenvío perfecto.
std::bind
usa valores semánticos por defecto. Es un predeterminado sano que le permite hacer cosas como las siguientes de forma segura.
int f(double x);
auto fun = std::bind(f, 1.0); // stores a copy, not a reference to a temporary
fun();
El uso de la semántica de valores es seguro: el tiempo de vida de los argumentos enlazados se convierte en el tiempo de vida del objeto devuelto por bind. Usar la semántica de referencia no tendría esa garantía. Por lo tanto, debe ser explícito cuando desee semántica de referencia; Si te metes en problemas, entonces es tu culpa. Para hacer eso necesitas usar std::ref
:
int main(void)
{
double t=1.0;
printf("%f/n",t);
test(t);
printf("%f/n",t);
callback(std::bind(test, std::ref(t)));
printf("%f/n",t);
}
Este mismo protocolo se usa en otras partes de la biblioteca estándar, como el constructor std::thread
.