2013-02-07 105 views
5

使用MSVC2012,爲什麼std :: packaged_task <void()>無效?

將下面的代碼編譯和運行如預期

std::packaged_task< int() > task([]()->int{ std::cout << "hello world" << std::endl; return 0; }); 
std::thread t(std::move(task)); 
t.join(); 

,而下面的代碼將無法編譯和運行

std::packaged_task< void() > task([](){ std::cout << "hello world" << std::endl; }); 
std::thread t(std::move(task)); 
t.join(); 

爲什麼會這樣呢?

編輯: 作爲一種變通方法,可以使用std ::承諾得到一個std ::將來返回void

std::promise<void> promise; 
auto future = promise.get_future(); 
std::thread thread([](std::promise<void> &p){ std::cout << "hello world" << std::endl; p.set_value(); }, std::move(promise)); 
future.wait(); 

注意,有在vs2012庫中的缺陷的功能使用std :: thread強制你將這個承諾作爲一個l值引用傳遞,並將promise傳遞給它,如果你通過值或r值引用傳遞promise,它將不會編譯。據推測,這是因爲實現使用了std :: bind(),它的行爲並不像預期的那樣。

+6

有趣......第二個編譯時出現什麼錯誤? – Yuushi

+0

這可能是MSVC++中的一個錯誤。 –

+4

從我追溯到它們的實現,它最終歸結爲它們的函數對象執行狀態的存儲,特別是在名爲'_State_manager'的模板類中。 '_State_manager'沒有專門針對'void'狀態,這就像一個bug。我也可以完全出去吃午飯,但那似乎一切都崩潰了。 – WhozCraig

回答

5

這是MSVC2012中的一個錯誤。 MSVC2012附帶的線程庫實現中存在相當多的錯誤。我在我的博客文章中發佈了一個部分列表,將其與我的商業Just :: Thread庫相比較:http://www.justsoftwaresolutions.co.uk/news/just-thread-v1.8.0-released.html

+0

「對於VS2012庫,當std :: async與啓動策略std :: launch :: async一起使用時,返回的std :: future實例的析構函數不會等待該線程完成。」 - 我會做出這個選擇。標準文本中存在一個錯誤的觀點是引人注目的。 – Yakk

+1

「std :: async」的標準措辭經過精心挑選,而且微軟故意決定違背標準。這不是標準中的錯誤,儘管有些人認爲這是一個不好的選擇。 –

+0

這是令人失望的,其中一些錯誤會被最簡單的測試用例所捕獲。我對微軟的期望越來越高。 – aCuria

3

這個工作在GCC 4.7.2:

#include <thread> 
#include <future> 
#include <iostream> 

int main() { 
    std::packaged_task< void() > task([](){ std::cout << "hello world" << std::endl; }); 
    std::thread t(std::move(task)); 
    t.join(); 
    std::packaged_task< int() > task2([]()->int{ std::cout << "hello world" << std::endl; return 0; }); 
    std::thread t2(std::move(task2)); 
    t2.join(); 
} 

加上@WhozCraig的考古學暗示這可能是在MSVC2012的錯誤。

要解決該問題,請嘗試使用struct Nothing {};nullptr_t作爲您的返回值?

2

問題仍然存在於MSVS 2013RC中,但是我在MS修正它時做了這個臨時修補程序。它是void(...)的packaged_task的專門化,所以我建議將它放在一個頭文件中並將其包含在標準頭文件後面。注意make_ready_at_thread_exit()未實現,並且某些函數尚未完全測試,請使用at你自己的風險。

namespace std { 

template<class... _ArgTypes> 
class packaged_task<void(_ArgTypes...)> 
{ 
    promise<void> _my_promise; 
    function<void(_ArgTypes...)> _my_func; 

public: 
    packaged_task() { 
    } 

    template<class _Fty2> 
    explicit packaged_task(_Fty2&& _Fnarg) 
     : _my_func(_Fnarg) { 
    } 

    packaged_task(packaged_task&& _Other) 
     : _my_promise(move(_Other._my_promise)), 
     _my_func(move(_Other._my_func)) { 
    } 

    packaged_task& operator=(packaged_task&& _Other) { 
     _my_promise = move(_Other._my_promise); 
     _my_func = move(_Other._my_func); 
     return (*this); 
    } 

    packaged_task(const packaged_task&) = delete; 
    packaged_task& operator=(const packaged_task&) = delete; 

    ~packaged_task() { 
    } 

    void swap(packaged_task& _Other) { 
     _my_promise.swap(_Other._my_promise); 
     _my_func.swap(_Other._my_func); 
    } 

    explicit operator bool() const { 
     return _my_func != false; 
    } 

    bool valid() const { 
     return _my_func != false; 
    } 

    future<void> get_future() { 
     return _my_promise.get_future(); 
    } 

    void operator()(_ArgTypes... _Args) { 
     _my_func(forward<_ArgTypes>(_Args)...); 
     _my_promise.set_value(); 
    } 

    void reset() { 
     swap(packaged_task()); 
    } 
}; 

}; // namespace std 
相關問題