如何使用Lambda进行std ::调用惰性评估?

时间:2019-07-07 19:01:47

标签: c++ lambda callback c++17 c++20

C ++ 11引入了lambdas,这使我们能够更轻松地在C ++中实现延迟评估,因此我想知道是否可以以这种方式进行std :: invoke吗?

根据cppreference,std::invoke通过以下方式实现:

template <typename F, typename... Args>
decltype(auto) invoke(F&& f, Args&&... args) 
  noexcept(std::is_nothrow_invocable_v<F, Args...>)
{
    return detail::INVOKE(std::forward<F>(f), std::forward<Args>(args)...);
}

我们在这里可以看到很多完美的转发,我想进行一次懒惰的评估。这是我的实现:

template <typename F, typename... Args>
constexpr auto delay_invoke(F&& f, Args&&... args) {
    return [&]() -> decltype(auto) {
        return std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
    };
}

它在魔盒中通过了我的test。由于此lambda是通过引用捕获的,因此我认为以这种惰性评估方式存在悬而未决的参考问题,因此,我尝试通过值捕获进行第二种实现:

template <typename F, typename... Args>
constexpr auto delay_invoke(F&& f, Args&&... args) {
    return [=]() mutable -> decltype(auto) {
        return std::invoke(static_cast<F&&>(f), static_cast<Args&&>(args)...);
    };
}

,它也通过了我的测试。 我对吗?您有更好的实现或其他好的解决方案吗?谢谢!

1 个答案:

答案 0 :(得分:4)

  

您是否有更好的实现或其他好的解决方案?

您的两个示例要么通过引用捕获一切(这很糟糕,因为悬空了,尤其是在这样的用例中),要么复制了一切(比另一个参数,但是效率很低,因为某些参数可能是右值)。您要做的就是转发整个背包。但是,在C ++ 17中,“向前”捕获包非常繁琐。幸运的是,在C ++ 20中,由于有了P0780,这将变得容易得多。那篇论文中激励人心的例子非常接近您的例子。

向前捕获的C ++ 17版本需要tuple(或类似的东西):

template <typename... Args>
auto delay_invoke(Args&&... args) {
    return [tup=std::make_tuple(std::forward<Args>(args)...)]() mutable -> decltype(auto) {
        return std::apply([](auto&... args) -> decltype(auto) {
            return std::invoke(static_cast<Args&&>(args)...);
        }, tup);
    };
}

C ++ 20版本更简单:

template <typename... Args>
auto delay_invoke(Args&&... args) {
    return [...args=std::forward<Args>(args)]() mutable -> decltype(auto) {
        return std::invoke(std::forward<Args>(args)...);
    };
}