Wenn Sie
auto unevaluted_x = []() { return foo(); };
...
auto x = unevaluted_x();
Jedesmal, wenn Sie den Wert erhalten möchten, schreiben (wenn Sie unevaluated_x
nennen) berechnet ist, verschwenden Rechenressourcen. Also, um diese übermäßige Arbeit loszuwerden, ist es eine gute Idee zu verfolgen, ob das Lambda bereits aufgerufen wurde (vielleicht in einem anderen Thread oder an einer anderen Stelle in der Codebasis). Um dies zu tun, müssen wir einige Wrapper um 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;
};
Bitte beachten Sie, dass dieser Code ist nur ein Beispiel und ist nicht Thread-sicher.
Aber statt das Rad neu zu erfinden, könnte man einfach std::shared_future
verwenden:
auto x = std::async(std::launch::deferred, []() { return foo(); }).share();
Dieser weniger Code erfordert einige andere Funktionen zu schreiben und unterstützt (wie prüfen, ob hat sich der Wert bereits berechnet worden ist, Thread-Sicherheit, etc).
Es gibt den folgenden Text in dem Standard [futures.async, (3.2)]:
Wenn launch::deferred
in Politik, speichert DECAY_COPY(std::forward<F>(f))
und DECAY_COPY(std::forward<Args>(args))...
im freigegebenen Zustand gesetzt. Diese Kopien von f
und args
bilden eine verzögerte Funktion. Der Aufruf der verzögerten Funktion wertet INVOKE(std::move(g), std::move(xyz))
aus, wobei g
der gespeicherte Wert DECAY_COPY(std::forward<F>(f))
ist und xyz
die gespeicherte Kopie von DECAY_COPY(std::forward<Args>(args))....
ist. Jeder Rückgabewert wird als Ergebnis im gemeinsamen Status gespeichert. Jede Ausnahme, die von der Ausführung der zurückgestellten -Funktion propagiert wird, wird als Ausnahmeergebnis im gemeinsam genutzten Zustand gespeichert. Der gemeinsame Status wird nicht bereit gemacht, bis die Funktion abgeschlossen ist.Der erste Aufruf einer nicht zeitgesteuerten Wartefunktion (30.6.4) an einem asynchronen Rückgabeobjekt, das sich auf diesen gemeinsamen Zustand bezieht, muss die zurückgestellte Funktion im Thread aufrufen, der die Wartefunktion aufgerufen hat. Sobald die Auswertung von INVOKE(std::move(g),std::move(xyz))
beginnt, wird die Funktion nicht länger als verzögert angesehen. [Hinweis: Wenn diese Richtlinie zusammen mit anderen Richtlinien angegeben wird, z. B. bei Verwendung eines Richtlinienwerts launch::async | launch::deferred
, sollten Implementierungen den Aufruf oder die Auswahl der Richtlinie verzögern, wenn kein Concurrency mehr effektiv ausgenutzt werden kann. -end note]
Sie haben also eine Garantie, dass die Berechnung nicht aufgerufen wird, bevor sie benötigt wird.
Futures sind für das Warten auf das Ergebnis eines (möglicherweise asynchronen) Prozesses. Sie sind single-use und ziemlich schwer. Wenn Sie im selben Thread nach fauler Bewertung suchen, ist es wahrscheinlich nicht das, was Sie brauchen. Es gibt eine Bibliothek namens boost.outcome, die entwickelt wird. Es ist im Wesentlichen leichte Futures (nicht für Cross-Thread-Arbeit). Wenn Sie Ihre Lazy-Funktion wiederholt aufrufen wollen, dann ist wahrscheinlich ein Funktionsobjekt oder Lambda geeignet. Vielleicht möchten Sie auch boost.hana oder ähnliches sehen. –