如何使用 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)...);
};
}
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)...);
};
}