当你写
auto unevaluted_x = []() { return foo(); };
...
auto x = unevaluted_x();
每次您想要获取值时(当您调用 时unevaluated_x
)都会计算它,从而浪费计算资源。因此,为了摆脱这种过多的工作,最好跟踪 lambda 是否已经被调用(可能在其他线程中,或者在代码库中非常不同的位置)。为此,我们需要一些 lambda 包装器:
template<typename Callable, typename Return>
class memoized_nullary {
public:
memoized_nullary(Callable f) : function(f) {}
Return operator() () {
if (calculated) {
return result;
}
calculated = true;
return result = function();
}
private:
bool calculated = false;
Return result;
Callable function;
};
请注意,此代码只是一个示例,不是线程安全的。
但是,您不必重新发明轮子,而是可以使用std::shared_future
:
auto x = std::async(std::launch::deferred, []() { return foo(); }).share();
这需要更少的代码来编写并支持一些其他功能(例如,检查值是否已经计算,线程安全等)。
标准 [futures.async, (3.2)] 中有以下文本:
如果launch::deferred
在策略中设置,则存储DECAY_COPY(std::forward<F>(f))
并DECAY_COPY(std::forward<Args>(args))...
处于共享状态。这些副本f
构成args
了延迟函数。延迟函数的调用评估存储的值INVOKE(std::move(g), std::move(xyz))
在哪里,并且存储的副本是任何返回值存储为共享状态中的结果。从延迟函数的执行传播的任何异常都作为异常结果存储在共享状态中。在函数完成之前,共享状态不会准备好。在引用此共享状态的异步返回对象上首次调用非定时等待函数 (30.6.4) 应调用调用等待函数的线程中的延迟函数g
DECAY_COPY(std::forward<F>(f))
xyz
DECAY_COPY(std::forward<Args>(args))....
. 一旦评估INVOKE(std::move(g),std::move(xyz))
开始,该函数就不再被认为是延迟的。[注意:如果此策略与其他策略一起指定,例如在使用策略值时launch::async | launch::deferred
,实现应推迟调用或在无法有效利用更多并发时选择策略。——尾注]
因此,您可以保证在需要之前不会调用计算。