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)...);
};
}
,它也通过了我的测试。 我对吗?您有更好的实现或其他好的解决方案吗?谢谢!
答案 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)...);
};
}