如何使用 lambda 进行 std::invoke 惰性计算?

How using lambda to make std::invoke lazy evaluation?

C++11 引入了 lambdas 让我们更容易在 C++ 中实现 惰性求值,所以我想知道在这样的情况下是否可以实现 std::invoke方式?

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

template <typename F, typename... Args>
decltype(auto) invoke(F&& f, Args&&... args) 
  noexcept(...)
{
    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)...);
    };
}

它在 wandbox 中通过了我的测试。由于这个 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)...);
    };
}

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

Do you have nicer implementation or other good solution?

你的两个例子要么通过引用捕获所有内容(这很糟糕,因为悬空,尤其是在像这样的use-case中)或复制所有内容(比另一个好,但效率低下,因为一些参数可能是右值)。你要做的是转发整个包。但是,在 C++17 中,捕获包 "by forward" 非常乏味。值得庆幸的是,在 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)...);
    };
}