C++ C++;14/17-仅lambdas或期货等。?

C++ C++;14/17-仅lambdas或期货等。?,c++,c++11,lambda,lazy-evaluation,std-future,C++,C++11,Lambda,Lazy Evaluation,Std Future,我刚刚读到: P>注意到它有点老了,大部分答案都是2011年前的C++。现在我们有了语法lambda,它甚至可以推断返回类型,所以懒惰的计算似乎可以归结为只是传递它们:而不是 auto x = foo(); 你执行 auto unevaluted_x = []() { return foo(); }; 然后评估何时/何地需要: auto x = unevaluted_x(); 看来没什么了。但是,其中一个建议使用异步启动。有人能解释为什么/如果期货对懒惰的评估工作有意义,在C++中还是

我刚刚读到:

<> P>注意到它有点老了,大部分答案都是2011年前的C++。现在我们有了语法lambda,它甚至可以推断返回类型,所以懒惰的计算似乎可以归结为只是传递它们:而不是

auto x = foo();
你执行

auto unevaluted_x = []() { return foo(); };
然后评估何时/何地需要:

auto x = unevaluted_x();
看来没什么了。但是,其中一个建议使用异步启动。有人能解释为什么/如果期货对懒惰的评估工作有意义,在C++中还是更抽象地?似乎未来很可能被热切地评估,但简单地说,是在另一条线上,而且可能比创造它们的东西优先级更低;无论如何,它应该依赖于实现,对吗

<> P>还有其他的现代C++构造,它们在懒惰评价的上下文中是有用的吗?< < /P>

当你写< /P>时

auto unevaluted_x = []() { return foo(); };
...
auto x = unevaluted_x();
每次您想要获取值时(当您调用
unevaluated\ux
时),它都是经过计算的,浪费了计算资源。因此,为了摆脱这种过度的工作,最好跟踪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;
};
这需要编写更少的代码,并支持一些其他功能(例如,检查值是否已计算、线程安全性等)

标准[futures.async,(3.2)]中有以下文本:

如果在策略中设置了
launch::deferred
,则在共享状态下存储
decage\u COPY(std::forward(f))
decage\u COPY(std::forward(args))…
。这些
f
args的副本构成
延迟的功能。延迟函数的调用计算
INVOKE(std::move(g),std::move(xyz))
其中
g
decation\u COPY(std::forward(f))
的存储值,而
xyz
是 存储任何返回值的
decation\u copy(std::forward(args))的存储副本
作为共享状态的结果。从延迟的执行中传播的任何异常
函数作为异常结果存储在共享状态中。未创建共享状态
准备就绪,直到功能完成第一次调用非定时等待函数(30.6.4)
在引用此共享状态的异步返回对象上,应调用延迟函数
在调用等待函数的线程中。一旦
INVOKE(std::move(g),std::move(xyz))的计算开始
,函数就不再被认为是延迟的。[注:如果本政策适用 与其他策略一起指定,例如当使用策略值
launch::async | launch::deferred
时,实现应在以下情况下延迟策略的调用或选择: 无法有效利用更多的并发性。-结束说明]


因此,您可以保证在需要计算之前不会调用它。

这里有一些事情

应用程序顺序
求值是指在将参数传递到函数之前对其求值。
正常顺序
求值意味着在求值之前将参数传递到函数中

正常顺序求值的优点是某些参数从未被求值,缺点是某些参数会被反复求值

惰性
评估通常意味着
正常顺序+记忆
。推迟评估,希望你根本不需要评估,但如果你确实需要,记住结果,这样你只需要做一次。重要的部分是评估一个术语,从来没有或只评估一次,记忆是最简单的机制来提供这一点

promise/future
模式又不同了。这里的想法是,一旦有足够的信息可用,就开始进行评估,可能是在另一个线程中。然后,您将尽可能长时间地查看结果,以提高它已经可用的可能性


promise/future
模型与惰性评估有一些有趣的协同作用。战略是:

  • 推迟评估,直到肯定需要结果
  • 在另一个线程中开始评估
  • 做些别的事情
  • 后台线程完成并将结果存储在某处
  • 初始线程检索结果
  • 当结果由背景线程生成时,可以巧妙地引入记忆


    尽管两者之间存在协同作用,但它们的概念并不相同。

    在多线程应用程序中,同时请求数据需要花费大量精力来准备,可以使用线程安全的备忘录,这样用户不仅可以避免重做已经执行的工作,还可以避免启动自己的正在进行的工作版本


    使用未来或期货来传递数据是很容易的部分:C++已经实现了。诀窍是(a)找到一种方法来确保请求相同数据的多个线程创建对象,这些对象将被视为映射中的等效(或相同)键。。。这将取决于用户。。。和(b)使用具有该密钥和未来作为数据的并发映射。最多一个同时执行的insert、emplace或try_emplace尝试使用相同的键可以插入一个键值对,并且所有这些键值对都将返回一个迭代器到相同的键值对(可能已经在映射中一段时间了)。使用std::unordered_映射和互斥可以工作,但它不能很好地伸缩。java已经具备了在这些情况下具有优异性能的并发映射C++:同样需要,最好是在标准库中,尽可能快地使用。

    auto x = std::async(std::launch::deferred, []() { return foo(); }).share();