Итак, я отвечал на вопрос об ленивой оценке (здесь, мой ответ - это излишество для этого случая, но идея кажется интересной), и это заставило меня задуматься о том, как ленивый оценка может быть выполнена на С++. Я придумал путь, но я не был уверен в всех подводных камнях в этом. Существуют ли другие способы достижения ленивой оценки? как это можно сделать? Каковы подводные камни и этот и другие проекты?
Вот моя идея:
#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 какие-либо ошибки? Я добавил дополнительные круглые скобки вокруг E в макросе LAZY, чтобы убедиться, что отдельные имена обработаны справедливо, поскольку ссылки, подобные VEC [10], будут. Есть ли другие вещи, которые я не учитываю?
- В моем примере есть много слоев косвенности. Похоже, этого можно избежать.
- Правильно ли это запоминание результата, так что независимо от того, сколько или сколько вещей ссылается на ленивое значение, оно будет оцениваться только один раз (это я уверен в ленивой оценке плюс тонны общих указателей, возможно, петля)
Каковы ваши мысли?