所以我回答了一个关于延迟评估的问题(here,对于这种情况,我的回答是过分的,但是这个想法似乎很有趣),这让我思考了如何在C++中完成延迟评估。我想出了一种方法,但是我不确定所有的陷阱。还有其他方法可以实现惰性评估吗?怎么做?有哪些陷阱以及此设计和其他设计?

这是我的主意:

#include <iostream>
#include <functional>
#include <memory>
#include <string>

#define LAZY(E) lazy<decltype((E))>{[&](){ return E; }}

template<class T>
class lazy {
private:
    typedef std::function<std::shared_ptr<T>()> thunk_type;
    mutable std::shared_ptr<thunk_type> thunk_ptr;
public:
    lazy(const std::function<T()>& x)
        : thunk_ptr(
            std::make_shared<thunk_type>([x](){
                return std::make_shared<T>(x());
            })) {}
    const T& operator()() const {
        std::shared_ptr<T> val = (*thunk_ptr)();
        *thunk_ptr = [val](){ return val; };
        return *val;
    }
    T& operator()() {
        std::shared_ptr<T> val = (*thunk_ptr)();
        *thunk_ptr = [val](){ return val; };
        return *val;
    }
};

void log(const lazy<std::string>& msg) {
    std::cout << msg() << std::endl;
}

int main() {
    std::string hello = "hello";
    std::string world = "world";
    auto x = LAZY((std::cout << "I was evaluated!\n", hello + ", " + world + "!"));
    log(x);
    log(x);
    log(x);
    log(x);
    return 0;
}

我在设计中担心的一些事情。
  • decltype有一些奇怪的规则。我对decltype的使用是否有陷阱?我在LAZY宏中的E周围添加了额外的括号,以确保对单个名称进行公平对待,就像引用vec [10]一样。还有其他我不负责的事情吗?
  • 在我的示例中有很多间接层。看来这是可以避免的。
  • 这是否正确地记录了结果,所以无论什么或多少东西引用惰性值,它都只会评估一次(我对此很有信心,但是惰性评估加上大量共享指针可能使我陷入循环)

  • 你怎么认为?

    最佳答案

  • 您可能需要thunk_type并将其引用作为单独的对象。现在,lazy<T>的副本不会从原产地评估中获得任何 yield 。但是在这种情况下,您将获得其他间接访问。
  • 有时您可能只需要使用模板就可以摆脱包装到std::function中的麻烦。
  • 我不确定该值是否需要为shared_ptr。也许 call 者应该决定。
  • 您将在每次访问时产生新的闭包。

  • 考虑下一个修改:
    template<typename F>
    lazy(const F& x) :
      thunk_ptr([&x,&this](){
        T val = (*x)();
        thunk_ptr = [val]() { return val; };
        return val;
      })
    {}
    

    或者替代实现可能看起来像:
    template<typename F>
    auto memo(const F &x) -> std::function<const decltype(x()) &()> {
        typedef decltype(x()) return_type;
        typedef std::function<const return_type &()> thunk_type;
        auto thunk_ptr = std::make_shared<thunk_type>();
        auto *thunk_cptr = thunk_ptr.get();
    
        // note that this lambda is called only from scope which holds thunk_ptr
        *thunk_ptr = [thunk_cptr, &x]() {
            auto val = std::move(x());
            auto &thunk = *thunk_cptr;
            thunk = [val]() { return val; };
            // at this moment we can't refer to catched vars
            return thunk();
        };
    
        return [thunk_ptr]() { return (*thunk_ptr)(); };
    };
    

    关于c++ - 一种在C++中实现惰性评估的方法,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/16701108/

    10-10 00:58