Kiedy piszesz
auto unevaluted_x = []() { return foo(); };
...
auto x = unevaluted_x();
każdym razem, gdy chcą uzyskać wartość (podczas rozmowy unevaluated_x
) to obliczone, marnowania zasobów obliczeniowych. Tak więc, aby pozbyć się tej nadmiernej pracy, dobrze jest śledzić, czy lambda została już wywołana (może w innym wątku lub w innym miejscu w bazie kodu). Aby to zrobić, potrzebujemy opakowania o wartości 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;
};
Należy pamiętać, że ten kod jest tylko przykładem i nie jest bezpieczny dla wątków.
Ale zamiast wymyślania koła, można po prostu użyć std::shared_future
:
auto x = std::async(std::launch::deferred, []() { return foo(); }).share();
wymaga mniej kodu do zapisu i obsługuje kilka innych funkcji (jak sprawdzić, czy wartość została już obliczone, bezpieczeństwo nici, itp).
Jest dodaje się tekst w standardowym [futures.async, (3,2)]:
Jeśli launch::deferred
jest w polityce, sklepy DECAY_COPY(std::forward<F>(f))
i DECAY_COPY(std::forward<Args>(args))...
we wspólnym państwie. Te kopie f
i args
stanowią odroczoną funkcję. Inwokacja funkcji odroczonej ocenia INVOKE(std::move(g), std::move(xyz))
, gdzie g
jest zapisaną wartością DECAY_COPY(std::forward<F>(f))
i xyz
jest przechowywaną kopią DECAY_COPY(std::forward<Args>(args))....
Każda wartość zwracana jest przechowywana jako wynik w stanie współdzielonym. Każdy wyjątek propagowany po wykonaniu opóźnionej funkcji jest przechowywany jako wyjątkowy wynik w stanie współdzielonym. Stan wspólny nie jest gotowy do ukończenia funkcji .Pierwsze wywołanie funkcji czasu bez oczekiwania (30.6.4) na asynchronicznym obiekcie zwracającym, odnoszącym się do tego stanu współużytkowanego, powinno wywołać funkcję odroczoną w wątku, który wywołał funkcję oczekiwania. Po rozpoczęciu oceny INVOKE(std::move(g),std::move(xyz))
funkcja nie jest już uważana za odroczoną. [Uwaga: Jeśli ta zasada ma wartość określona razem z innymi zasadami, na przykład podczas korzystania z wartości strategii launch::async | launch::deferred
, implementacje powinny odroczyć wywołanie lub wybrać zasadę, jeśli nie można efektywniej wykorzystać współbieżności. -lub wstępna]
Masz więc gwarancję, że obliczenia nie zostaną wywołane, zanim będzie to konieczne.
kontrakty futures służą do oczekiwania na wynik niektórych procesów (prawdopodobnie asynchronicznych). Są jednorazowe i dość ciężkie. Jeśli szukasz leniwej oceny w tym samym wątku, prawdopodobnie nie to, czego potrzebujesz. Tworzona jest biblioteka o nazwie boost.outcome. Jest to w zasadzie lekki futures (nie przeznaczony do pracy z nitkami krzyżowymi). Jeśli chcesz wielokrotnie wywoływać swoją leniwą funkcję, prawdopodobnie odpowiedni jest obiekt funkcji lub lambda. Możesz również zajrzeć do boost.hana lub podobnego. –