所以我在回答一个关于惰性求值的问题(在这里,我的回答对于这种情况来说有点矫枉过正,但这个想法似乎很有趣),这让我想到了如何在 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] 一样的引用。还有其他我没有考虑的事情吗?
- 在我的示例中有很多间接层。这似乎是可以避免的。
- 这是否正确地记住了结果,以便无论有什么或有多少东西引用了惰性值,它只会评估一次(我对此非常有信心,但惰性评估加上大量共享指针可能会让我陷入循环)
你怎么认为?