Passando rvalues ​​attraverso std :: bind

Voglio passare un rvalue tramite std::bind a una funzione che accetta un riferimento di rvalue in C ++ 0x. Non riesco a capire come farlo. Per esempio:

 #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()) } 

Il motivo per cui questo fallisce è perché quando si specifica foo , la funzione a cui si sta vincolando è:

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

Tuttavia, il valore passato da std::bind non sarà un rvalue, ma un lvalue (memorizzato come membro da qualche parte nel funtore di bind risultante). Quello, è il functor generato è simile a:

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

Costruito come your_bind(Movable()) . Quindi puoi vedere che questo non funziona perché Movable&& non può collegarsi a Movable . †

Una soluzione semplice potrebbe essere invece questa:

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

Perché ora la funzione che stai chiamando è:

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

E la chiamata funziona bene (e, naturalmente, si potrebbe fare ciò che è foo se lo si desidera). Ma una domanda interessante è se possiamo far funzionare il tuo bind originale, e possiamo farlo tramite:

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

Cioè, abbiamo appena std::move l’argomento prima di effettuare la chiamata, in modo che possa bind. Ma sì, è brutto. Il cast è richiesto perché std::move è una funzione sovraccaricata, quindi dobbiamo specificare quale sovraccarico vogliamo colare al tipo desiderato, eliminando le altre opzioni.

In realtà non sarebbe così male se std::move non fosse sovraccarico, come se avessimo qualcosa di simile:

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

Che è molto più semplice Ma a meno che tu non abbia una tale funzione in giro, penso sia chiaro che probabilmente vuoi semplicemente specificare un argomento template più esplicito.


† Questo è diverso dal chiamare la funzione senza un argomento template esplicito, perché specificandolo esplicitamente rimuove la possibilità che venga dedotta. ( T&& , dove T è un parametro template, può essere dedotto a qualsiasi cosa , se lo si lascia fare .)

Ragazzi ho violato una perfetta versione di inoltro di un raccoglitore (limitato a 1 parametro) qui http://code-slim-jim.blogspot.jp/2012/11/stdbind-not-compatable-with-stdmove.html

Per riferimento il codice è

 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_)); } };

Come puoi vedere dalla dimostrazione di concetto di cui sopra, è molto ansible …

Non vedo alcun motivo per cui std :: bind sia incompatibile con std :: move … std :: forward è dopotutto per l’inoltro perfetto Non capisco perché non c’è uno std :: forwarding_bind ???

(Questo è in realtà un commento alla risposta di GMan, ma ho bisogno di un po ‘di formattazione per il codice). Se il functor generato è in questo modo:

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

     void operator () ()
     {
         foo (arg0);
     }

     Argro mobile;
 };

poi

 int main ()
 {
     auto f = your_bind (Movable ());
     f ();  // Nessun errore!
 }

complimenti senza errori. in quanto è ansible assegnare e inizializzare i dati con rvalue e quindi passare un valore di dati all’argomento rvalue di foo ().
Tuttavia, suppongo che l’implementazione di bind estrae il tipo di argomento della funzione direttamente dalla firma di foo (). cioè il functor generato è:

 struct your_bind
 {
     your_bind (Movable && arg0):
     arg0 (arg0) // **** Errore: imansible convertire da Movable a Movable &&
     {}

     void operator () ()
     {
         foo (arg0); 
     }

     Movable && arg0;
 };

e in effetti, questo non riesce a inizializzare il membro dei dati di valore. Forse, la implectionment del binding semplicemente non estrae correttamente il tipo “unreferenced” dal tipo di argomento della funzione e usa questo tipo per la dichiarazione dei membri dei dati di functor “as is”, senza trimming &&.

il corretto functor dovrebbe essere:

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

     void operator () ()
     {
         foo (arg0); 
     }

     Argro mobile;  // trim && !!!
 };


Potresti usare un’espressione lambda.

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

Questa sembra essere l’opzione più semplice.

Un altro miglioramento nella risposta di GManNickG e ho una soluzione carina:

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

(funziona in gcc-4.9.2 e msvc2013)