使用 std::bind 捕获参数包"by move"



我试图从头开始实现std::async,并且遇到了仅移动类型参数的问题。它的要点是,c++ 14的初始捕获允许我们"通过移动"或"通过完全转发"捕获单个变量,但是它们似乎不允许我们"通过移动"或"通过完全转发"捕获参数包,因为你不能通过初始捕获捕获参数包,只能通过命名捕获。

我发现了什么似乎是一个解决方案,通过使用std::bind捕获参数包"移动",然后使用包装器将参数从绑定对象的存储中移动到我真正想要调用的函数的参数槽中。如果你不去想太多,它甚至看起来很优雅。但我禁不住想,一定有更好的办法——理想的办法是完全不依赖于std::bind

(最坏的情况,我想知道我必须自己重新实现多少std::bind才能摆脱它。这个练习的部分目的是展示事情是如何实现的,所以有一个像std::bind这样复杂的依赖真的很糟糕。

我的问题是:

  • 我如何使我的代码工作,不使用std::bind ?(即,只使用核心语言特性。

  • 是我的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);
}

有人离线向我建议,另一种方法是在std::tuple中捕获args包,然后使用类似std::experimental::apply的东西将该元组重新扩展到task的参数列表中(即将在您附近的c++ 17标准库中出现!)。

    auto bound = [task = std::move(task), args = std::make_tuple(std::move(args)...)]() {
        std::experimental::apply(task, args);
    };

这样更干净。我们减少了库代码的数量,从bind减少到"仅仅"tuple。但这仍然是一个很大的依赖,我希望能够摆脱!

最新更新