让代码首先以幼稚的方式说明自己:
int heavy_calc() // needed to be called once
{
// sleep(7500000 years)
return 42;
}
int main()
{
auto foo = [] {
// And cached for lambda return value
static int cache = heavy_calc();
return cache;
};
return foo() + foo();
}
我想在第一次调用时计算出lambda内部缓存的值。天真的方法是使用static
缓存,但使用increases binary size and refuses to be be inlined。
我想出了在捕获列表中创建cache
并将lambda标记为inlines without problems的mutable
的问题,但是需要缓存以默认值开头,这可能会破坏类不变性。>
auto foo = [cache=0] () mutable {
// And cached for lambda return value
if(!cache)
cache = heavy_calc();
return cache;
};
我的第三种方法使用boost::optional
in mutable lambda
auto foo = [cache=std::optional<int>{}] () mutable {
// And cached for lambda return value
if(!cache)
cache = heavy_calc();
return *cache;
};
它可以正常工作,但在我看来是一种捕获列表+ mutable
关键字hack。此外,mutable
会影响所有捕获的参数,从而使lambda在实际使用中的安全性降低。
也许对此有更好/更干净的解决方案?或者只是不同的方法而最终产生了完全相同的效果。
编辑,有一些背景:
选择Lambda方法是因为我正在修改一些回调lambda,当前它用作:
[this, param]{this->onEvent(heavy_calc(param));}
我想减少heavy_calc
个通话而不预先评估(仅在第一次通话时)
答案 0 :(得分:4)
说实话,我认为这里没有使用lambda的任何理由。您可以编写常规的可重用类以缓存计算值。如果您坚持使用lambda,则可以将值计算移至参数,因此无需进行任何mutable
:
int heavy_calc() // needed to be called once
{
// sleep(7500000 years)
return 42;
}
int main()
{
auto foo
{
[cache = heavy_calc()](void)
{
return cache;
}
};
return foo() + foo();
}
使用一些模板,可以编写一个类来懒惰地评估和缓存任意计算的结果:
#include <boost/optional.hpp>
#include <utility>
template<typename x_Action> class
t_LazyCached final
{
private: x_Action m_action;
private: ::boost::optional<decltype(::std::declval<x_Action>()())> m_cache;
public: template<typename xx_Action> explicit
t_LazyCached(xx_Action && action): m_action{::std::forward<xx_Action>(action)}, m_cache{} {}
public: auto const &
operator ()(void)
{
if(not m_cache)
{
m_cache = m_action();
}
return m_cache.value();
}
};
template<typename x_Action> auto
Make_LazyCached(x_Action && action)
{
return t_LazyCached<x_Action>{::std::forward<x_Action>(action)};
}
class t_Obj
{
public: int heavy_calc(int param) // needed to be called once
{
// sleep(7500000 years)
return 42 + param;
}
};
int main()
{
t_Obj obj{};
int param{3};
auto foo{Make_LazyCached([&](void){ return obj.heavy_calc(param); })};
return foo() + foo();
}
答案 1 :(得分:2)
它可以正常工作,但是将我视为一种捕获列表+可变关键字hack。可变还会影响所有捕获的参数,因此使lambda在实际使用中不太安全。
有一种解决方案可以滚动您自己的手工制作的lambda:
#include <optional>
int heavy_calc() // needed to be called once
{
// sleep(7500000 years)
return 42;
}
int main()
{
struct {
std::optional<int> cache;
int operator()() {
if (!cache) cache = heavy_calc();
return *cache;
}
} foo;
return foo() + foo();
}
以相同的方式内联,您无需依赖捕获+可变hack。
答案 2 :(得分:0)
我确实认为这正是可变lambda的用例。如果您不希望所有变量都可变,我建议您仅使用一个mutable
字段创建函子类。这样,您就可以同时兼顾两个方面(好的,不是那么简洁)。另一个好处是operator()
是const
(这很正确,因为它总是返回相同的值)
#include <optional>
int heavy_calc() {
// sleep(7500000 years)
return 42;
}
struct my_functor {
mutable std::optional<int> cache;
int operator()() const {
if (!cache) cache = heavy_calc();
return *cache;
}
}
int main() {
my_functor foo;
return foo() + foo();
}