2013-02-07 145 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 
相关问题