Bei der Verwendung einiger lokaler Lambda-Objekte in einer C ++ 11-Funktion war ich versucht, sie als const static auto lambda = ...
zu deklarieren, nur damit der Compiler weiß, dass nur ein std::function
-Objekt benötigt wird (und möglicherweise den Aufruf optimieren und / oder inline es), aber ich erkannte, dass das Erfassen von lokalen Werten durch Referenz in diesem Fall zu merkwürdigem Verhalten führt.
Betrachten Sie den folgenden Code:
%Vor% Dies funktioniert nicht mit mehreren Aufrufen von doSomething()
, aber die Mechanik ist nicht klar.
foo
beim ersten Aufruf gebunden und dann an eine Stapeladresse gebunden, die bei aufeinanderfolgenden Aufrufen ungültig wird? static
fallen zu lassen? Wo ist dieses Verhalten im Standard angegeben? Bedenkt man, dass es eine static
Variable ist, wo ist es konstruiert? Faul beim ersten Aufruf von doSomething()
(damit der erste Aufruf funktioniert) oder beim Programmstart?
Eine statische Funktion-Scope-Variable wird "träge" initialisiert, wenn der Kontrollfluss zuerst seine Deklaration erreicht. Dies bedeutet, dass die Erfassung durch Referenz tatsächlich an den foo
aktuell auf dem Stapel bindet, und wenn der Aufruf beendet wird, wird diese Bindung schwankend.
Versuchen Sie nicht, dem Compiler zu viel zu helfen; make lambda
static
sieht wie eine Mikrooptimierung aus, mit sehr schlechten Nebenwirkungen. Es gibt so gut wie keinen Aufwand, um ein Closure-Objekt tatsächlich zu erstellen, und der Compiler kann es einfach inline einfügen, unabhängig davon, ob es static
ist oder nicht.
Ganz zu schweigen von der Tatsache, dass Sie bei der Erstellung des std::function
-Objekts nicht sparen, auch wenn Sie mit Ihrem Ansatz arbeiten. Der Typ eines Lambda-Ausdrucks ist ein unbenanntes Abschlussobjekt, nicht std::function
. Also, auch wenn lambda
ist static
, das std::function
Objekt wird trotzdem in jedem Aufruf erstellt (es sei denn das Ganze ist inline).
Dies funktioniert nicht mit mehreren Aufrufen von
doSomething()
, aber die Mechanik ist nicht klar.
Dies liegt daran, dass foo
auf dem Stapel zugeordnet ist. Die genaue Adresse von foo
hängt von dem Aufruf-Stack ab, der zum Aufruf von doSomething
führte. Mit anderen Worten, die Adresse von foo
unterscheidet sich wahrscheinlich zwischen Funktionsaufrufen, es sei denn, der Aufrufstapel ist genau derselbe.