惰性求值,也称为传需求调用(call-by-need),它有两个相关而又有区别的含义:“延迟求值"和"最小化求值”,此处为实现"延迟求值"的功能。
#pragma once
#include<optional>
#include<functional>
template<class T>
class Lazy;
template<class RType,class ...ArgsType>
class Lazy<RType(ArgsType...)>
{
private:
using FuncType = std::function<RType(ArgsType...)>;
public:
explicit Lazy(FuncType&& func) noexcept
:m_func(std::forward<FuncType>(func)) {}
template<typename ...Args> const RType& value(Args&& ...args) noexcept {
if(!m_value.has_value()) {
m_value.emplace(m_func(std::forward<Args>(args)...));
}
return m_value.value();
}
private:
FuncType m_func;
std::optional<RType> m_value;
};
调用:
该实现主要利用了C++的可变参数模板技术,使得传入的函数类型得到高度泛化。同时,使用了C++17中的std::optional保存运算结果,更加贴近Rust中的实现;而利用模板偏特化,则使得接口的实现与std::function类似,更加优雅与可读。
该简易实现存在不足:传入不同的参数仍得到第一次的运算结果。为此,可以将传入的参数作为key,将运算结果作为value,并通过std::vector保存起来,实现不同参数返回不同运算结果的目的。代码如下:
template<class T>
class Lazy;
template<class RType,class ...ArgsType>
class Lazy<RType(ArgsType...)>
{
private:
using FuncType = std::function<RType(ArgsType...)>;
public:
explicit Lazy(FuncType&& func) noexcept
:m_func(std::forward<FuncType>(func)) {}
~Lazy() {}
template<typename ...Args> const RType& value(Args&& ...args) noexcept {
for(auto& [args_,value_] : m_value) {
if(args_.args() == std::tuple(args...)) return value_;
}
auto value = m_func(std::forward<Args>(args)...);
m_value.emplace_back(std::make_pair(ArgsCache(std::forward<Args>(args)...),std::move(value)));
return m_value.back().second;
}
private:
struct ArgsCache
{
ArgsCache(ArgsType&& ...args) noexcept
:m_args(std::forward<ArgsType>(args)...) {}
~ArgsCache() {}
const decltype(auto) args() const {
return m_args;
}
std::tuple<typename std::decay_t<ArgsType>...> m_args;
};
using PairType = std::pair<ArgsCache,RType>;
private:
FuncType m_func;
std::vector<PairType> m_value;
};
通过使用std::tuple保存不定长,不定类型的参数。因为std::tuple内部已经重载了==运算符,所以可以很方便地进行比较。
传入不同的参数:
运行结果: