Pasando rvalues ​​a través de std :: bind

Quiero pasar un valor r a través de std::bind a una función que toma una referencia rvalue en C ++ 0x. No puedo entender cómo hacerlo. Por ejemplo:

 #include  #include  template void foo(Type &&value) { Type new_object = std::forward(value); // move-construct if possible } class Movable { public: Movable(Movable &&) = default; Movable &operator=(Movable &&) = default; }; int main() { auto f = std::bind(foo, Movable()); f(); // error, but want the same effect as foo(Movable()) } 

La razón por la que esto falla es porque cuando especifica foo , la función a la que se está vinculando es:

 void foo(Movable&&) // *must* be an rvalue { } 

Sin embargo, el valor pasado por std::bind no será un valor r, sino un lvalue (almacenado como un miembro en algún lugar del functor de bind resultante). Eso, es el functor generado es similar a:

 struct your_bind { your_bind(Movable arg0) : arg0(arg0) {} void operator()() { foo(arg0); // lvalue! } Movable arg0; }; 

Construido como your_bind(Movable()) . Entonces puede ver que esto falla porque Movable&& no se puede unir a Movable . †

Una solución simple podría ser esta en su lugar:

 auto f = std::bind(foo, Movable()); 

Porque ahora la función a la que llamas es:

 void foo(Movable& /* conceptually, this was Movable& && and collapsed to Movable& */) { } 

Y la llamada funciona bien (y, por supuesto, puede hacer que foo si lo desea). Pero una pregunta interesante es si podemos hacer que su enlace original funcione, y podemos hacerlo a través de:

 auto f = std::bind(foo, std::bind(static_cast(std::move), Movable())); 

Es decir, simplemente std::move el argumento antes de hacer la llamada, para que se pueda unir. Pero bueno, eso es feo. El elenco es obligatorio porque std::move es una función sobrecargada, por lo que debemos especificar qué sobrecarga queremos mediante conversión al tipo deseado, eliminando las otras opciones.

En realidad, no sería tan malo si std::move no estuviera sobrecargado, como si tuviéramos algo así como:

 Movable&& my_special_move(Movable& x) { return std::move(x); } auto f = std::bind(foo, std::bind(my_special_move, Movable())); 

Que es mucho más simple. Pero a menos que tenga una función así, creo que está claro que probablemente solo quiera especificar un argumento de plantilla más explícito.


† Esto es diferente a llamar a la función sin un argumento explícito de plantilla, porque al especificarlo explícitamente se elimina la posibilidad de que se deduzca. ( T&& , donde T es un parámetro de plantilla, se puede deducir a cualquier cosa , si lo permite ).

Chicos he pirateado una versión de reenvío perfecta de una carpeta (limitada a 1 param) aquí http://code-slim-jim.blogspot.jp/2012/11/stdbind-not-compatable-with-stdmove.html

Para referencia, el código es

 template  class MovableBinder1 { typedef void (*F)(P&&); private: F func_; P p0_; public: MovableBinder1(F func, P&& p) : func_(func), p0_(std::forward

(p)) { std::cout < < "Moved" << p0_ << "\n"; } MovableBinder1(F func, P& p) : func_(func), p0_(p) { std::cout << "Copied" << p0_ << "\n"; } ~MovableBinder1() { std::cout << "~MovableBinder1\n"; } void operator()() { (*func_)(std::forward

(p0_)); } };

Como puede ver en la prueba de concepto anterior, es muy posible …

No veo ninguna razón por la que std :: bind sea incompatible con std :: move … std :: forward es, después de todo, un reenvío perfecto. No entiendo por qué no hay un std :: forwarding_bind ???

(Esto es en realidad un comentario a la respuesta de GMan, pero necesito algo de formato para el código). Si el functor generado en realidad es así:

 struct your_bind
 {
     your_bind (Arg0 movible):
     arg0 (arg0)
     {}

     operador vacío () ()
     {
         foo (arg0);
     }

     Arg0 movible;
 };

entonces

 int main ()
 {
     auto f = your_bind (Movable ());
     F();  // ¡Sin errores!
 }

comstack sin errores ya que es posible asignar e inicializar datos con rvalue y luego pasar un valor de datos a rvaluear el argumento de foo ().
Sin embargo, supongo que la implementación del enlace extrae el tipo de argumento de la función directamente desde la firma foo (). es decir, el functor generado es:

 struct your_bind
 {
     your_bind (Movable && arg0):
     arg0 (arg0) // **** Error: no se puede convertir de Movable a Movable &&
     {}

     operador vacío () ()
     {
         foo (arg0); 
     }

     Movable && arg0;
 };

y de hecho, esto realmente no puede inicializar el miembro de datos rvalue. Quizás, la implementación de vinculación simplemente no extraiga correctamente el tipo “sin referencia” del tipo de argumento de función y use este tipo para la statement de miembro de datos de functor “tal cual”, sin recortar &&.

el functor correcto debería ser:

 struct your_bind
 {
     your_bind (Movable && arg0):
     arg0 (arg0)
     {}

     operador vacío () ()
     {
         foo (arg0); 
     }

     Arg0 movible;  // recortar && !!!
 };


Podría usar una expresión lambda.

 auto f = [](){ foo(Movable()); }; 

Esta parece ser la opción más simple.

Una mejora más en la respuesta de GManNickG y tengo una bonita solución:

 auto f = std::bind( foo, std::bind(std::move, Movable()) ); 

(funciona en gcc-4.9.2 y msvc2013)