2013-04-22 6 views
8

Rozważmy poniższy kod:std :: asynchronicznie zanikające (tracące) wartości rvalue w Visual Studio 2012 Update 2. Jakiekolwiek obejścia?

#include <memory> 
#include <future> 

using namespace std; 

template <typename T, typename Work> 
void Test2(future<T> f, Work w) 
{ 
    async([](future<T> && f, Work w) 
         {}, move(f), move(w)); 
} 

int main() 
{ 
    future<int> x = std::async([]()->int{ 
     std::this_thread::sleep_for(std::chrono::microseconds(200)); 
     return 10; 
    }); 

    Test2(std::move(x), [](int x){}); 
    return 0; 
} 

Powyższe, nie powiedzie się z powodu następującego błędu kompilatora:

Error 1 error C2664: 'void Test2::::operator ()(std::future<_Ty> &&,Work) const' : cannot convert parameter 1 from 'std::future<_Ty>' to 'std::future<_Ty> &&' c:\program files (x86)\microsoft visual studio 11.0\vc\include\xrefwrap 98 1 ConsoleApplication6

GCC 4.7.2 kompiluje dobrze http://ideone.com/KhMiV6

Zanim pójdę do przodu i raport to na Microsoft Connect:

1) Czy to błąd ze strony VC11 lub jest to faktycznie standar d zachowanie?

2) Czy ktoś wie o obejście tego?

EDIT: I zgłaszali go na Microsoft Connect here. Aby przyspieszyć rozwiązanie, zachęcamy do jego upowszechnienia.

Odpowiedz

5

Hm, wygląda na to, że jest to błąd w VC11. Najwyraźniej implementacja async tak naprawdę nie przekazuje argumentów, ale je kopiuje.

Ze szczytu głowie Chciałbym stworzyć mały otoczka do rvalue że Move-konstruuje element gdy owinięcie jest skopiowany:

template <typename T> 
    struct rvref_wrapper { 
     rvref_wrapper(T&& value) : value_(std::move(value)) {} 
     rvref_wrapper(rvref_wrapper const& other) : value_ (other.get()) {} 
     T&& get() const { return std::move(value_); } 
     mutable T value_; 
    }; 

    template <typename T> 
    auto rvref(T&& x) -> rvref_wrapper<typename decay<T>::type> { 
     return std::move(x); 
    } 

Wtedy trzeba by zmodyfikować scenariusz testowy, więc że lambda Test2 wykonuje się opakowanie, a nie samego future:

template <typename T, typename Work> 
    void Test2(future<T> f, Work w) 
    { 
     async([](rvref_wrapper<future<T>> fr, Work w) { 
      // acquire future<T>&& here 
      future<T> f(fr.get()); 
      // e.g. call work with future value 
      w(f.get()); 
     }, rvref(f), move(w)); 
    } 

    int main() 
    { 
     future<int> x = std::async([]()->int{ 
      std::this_thread::sleep_for(std::chrono::microseconds(200)); 
      return 10; 
     }); 

     Test2(std::move(x), [](int x){}); 
     return 0; 
    } 

wygląda nieco brzydki, ale przynajmniej zbiera. Mam nadzieję, że ci pomaga!

+0

Zainspirowany przez std :: ref, myślałem o dokładnie tym samym rozwiązaniu. Jednym z problemów, które należy zanotować przy pomocy Twojego kodu, jest parametr 'f' Test2, który wykracza poza zakres i zostaje zniszczony, zanim osiągnie linię' future && f = fr.get(); '. Naprawiłem to w moim własnym kodzie (dość zabawne, że miałem dokładnie ten sam problem). Jestem zainteresowany tym, jak to naprawisz. Dziękuje za odpowiadanie. –

+0

Och, tak, ale możesz też zapisać 'mutable T' (zamiast' T && ') w swojej klasie otoki i przekazać to podczas kopiowania. Tak więc opakowanie ma nawet własność twojej przyszłości. –

+0

To było również moje rozwiązanie. Zmodyfikuj swoją odpowiedź tak, aby odzwierciedlała poprawne rozwiązanie, więc mogę ją zaakceptować :) –