使用std :: bind捕获参数包"移动"

时间:2015-07-22 07:00:24

标签: variadic-templates c++14 stdbind stdasync packaged-task

我试图从头开始实现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中讨论和/或支持参数包捕获的建议。

Here's my code:

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);
}

1 个答案:

答案 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。但这仍然是我喜欢能够摆脱的一大依赖!