我试图从头开始实现std::async
,并且遇到了仅移动类型参数的打嗝。它的要点是,C ++ 14 init-capture允许我们捕获单个变量"通过移动"或者"通过完美转发",但他们不似乎让我们捕获参数包"通过移动"并且"通过完美转发",因为你无法通过init-capture捕获参数包 - 只能通过命名捕获。
通过使用std::bind
捕获参数包"通过移动"然后使用包装器将参数移出参数包,我找到了似乎是一种解决方法将对象的存储绑定到我真正想要调用的函数的参数槽中。它甚至看起来很优雅,如果你不太想太多。但我无法帮助您认为必须有更好的方法 - 理想情况下,根本不依赖std::bind
。
(最坏的情况是,我想知道std::bind
我必须自己重新实现多少才能摆脱它。本练习的部分内容是显示事情是如何一直实现到底层的,因此具有像std::bind
那样复杂的依赖性真的很糟糕。)
我的问题是:
如何在不使用std::bind
的情况下使我的代码正常工作? (即,仅使用核心语言功能。通用lambda是公平游戏。)
我的std::bind
解决方法是防弹吗?也就是说,任何人都可以举例说明STL的std::async
是否有效以及Async
失败了吗?
我们将非常感谢您在C ++ 1z中讨论和/或支持参数包捕获的建议。
template<typename UniqueFunctionVoidVoid>
auto FireAndForget(UniqueFunctionVoidVoid&& uf)
{
std::thread(std::forward<UniqueFunctionVoidVoid>(uf)).detach();
}
template<typename Func, typename... Args>
auto Async(Func func, Args... args)
-> std::future<decltype(func(std::move(args)...))>
{
using R = decltype(func(std::move(args)...));
std::packaged_task<R(Args...)> task(std::move(func));
std::future<R> result = task.get_future();
#ifdef FAIL
// sadly this syntax is not supported
auto bound = [task = std::move(task), args = std::move(args)...]() { task(std::move(args)...) };
#else
// this appears to work
auto wrapper = [](std::packaged_task<R(Args...)>& task, Args&... args) { task(std::move(args)...); };
auto bound = std::bind(wrapper, std::move(task), std::move(args)...);
#endif
FireAndForget(std::move(bound));
return result;
}
int main()
{
auto f3 = [x = std::unique_ptr<int>{}](std::unique_ptr<int> y) -> bool { sleep(2); return x == y; };
std::future<bool> r3 = Async(std::move(f3), std::unique_ptr<int>{});
std::future<bool> r4 = Async(std::move(f3), std::unique_ptr<int>(new int));
assert(r3.get() == true);
assert(r4.get() == false);
}
答案 0 :(得分:2)
有人向我建议离线,另一种方法是捕获args
中的std::tuple
包,然后使用某些内容将该元组重新展开到task
的参数列表中就像std::experimental::apply
一样(很快就会到你附近的C ++ 17标准库!)。
auto bound = [task = std::move(task), args = std::make_tuple(std::move(args)...)]() {
std::experimental::apply(task, args);
};
这更清洁。我们减少了所涉及的图书馆代码数量,从bind
减少到&#34;仅仅#34; tuple
。但这仍然是我喜欢能够摆脱的一大依赖!