Warum funktioniert eine lambda haben eine Größe von 1 byte?
Arbeite ich mit der Erinnerung an einige Lambda-Ausdrücke in C++, aber ich bin ein bisschen verwirrt durch Ihre Größe.
Hier ist mein test-code:
#include <iostream>
#include <string>
int main()
{
auto f = [](){ return 17; };
std::cout << f() << std::endl;
std::cout << &f << std::endl;
std::cout << sizeof(f) << std::endl;
}
Können Sie es hier: http://fiddle.jyt.io/github/b13f682d1237eb69ebdc60728bb52598
Der Ausgang ist:
17
0x7d90ba8f626f
1
Dies deutet darauf hin, dass die Größe der meine lambda 1 ist.
-
Wie ist das möglich?
-
Sollte nicht die lambda sein, also mindestens einen Zeiger auf seine Umsetzung?
- seine, implementiert als eine Funktion Objekt (ein
struct
mit einemoperator()
) - Und eine leere struct kann nicht sein, Größe 0, daher die 1 Folge. Versuchen Sie erfassen etwas, und sehen, was passiert mit der Größe.
- Warum sollte ein lambda-Ausdruck ein Zeiger??? Es ist ein Objekt, das einen Aufruf der operators.
- Lambda-Ausdrücke in C++ existiert zur compile-Zeit, und Anrufungen verbunden sind (oder sogar eingebettet) zur compile oder link-Zeit. Es gibt daher keine Notwendigkeit für eine Laufzeit Zeiger in das Objekt selbst. @KerrekSB Es ist nicht eine unnatürliche denke, zu erwarten, dass ein lambda-Ausdruck enthält eine Funktion Zeiger, da die meisten Sprachen, die zur Umsetzung von lambdas sind dynamischer als C++.
- Das ist eine ziemlich vage Aussage. Ein lambda-Ausdruck ist ein Ausdruck, und der Ausdruck ist, existiert tatsächlich zur compile-Zeit, aber was zählt, ist der Wert, den Sie erhalten, die aus der Bewertung, der Ausdruck, das ist der Verschluss-Objekt, und existiert zur Laufzeit. Auswertung eines Ausdrucks geschieht (als-ob) zur Laufzeit (außer für Konstante Ausdrücke).
- Angelegenheiten" - in welchem Sinne? Der Grund für die Schließung-Objekt kann leer sein (anstatt mit einem function pointer) ist, da die Funktion aufgerufen werden, ist bekannt zur compile - /link-Zeit. Dies ist, was die OP scheint missverstanden haben. Ich sehe nicht, wie Sie Ihre Kommentare klären sich die Dinge.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Die lambda in Frage, hat eigentlich kein Staat.
Untersuchen:
Und wenn wir hatten
lambda f;
es ist eine leere Klasse. Nicht nur die obenlambda
funktional ähnlich lambda, es ist (im Grunde), wie Ihre lambda-Ausdruck implementiert! (Es braucht auch eine implizite cast-Funktion-Zeiger-operator und dem Namenlambda
wird ersetzt mit einigen compiler-generierten pseudo-guid)In C++, Objekte sind keine Zeiger. Sie sind die eigentlichen Dinge. Benutzen Sie nur den Raum benötigt zum speichern von Daten, die in Ihnen. Ein Zeiger auf ein Objekt kann größer sein als ein Objekt.
Während Sie vielleicht denken, dass lambda als ein Zeiger auf eine Funktion, ist es nicht. Sie können nicht zuweisen, die
auto f = [](){ return 17; };
auf eine andere Funktion oder lambda!oben ist illegale. Es gibt keine Zimmer im
f
zu speichern die Funktion genannt wird -, dass Informationen gespeichert werden Typ vonf
, nicht im Wert vonf
!Wenn du das getan hast:
oder so:
sind Sie nicht mehr die Speicherung der lambda direkt. In beiden Fällen
f = [](){ return -42; }
legal ist -- so dass in diesen Fällen speichern wir die Funktion, die wir aufrufen im Wert vonf
. Undsizeof(f)
ist nicht mehr1
, sondernsizeof(int(*)())
oder größer (im Grunde, werden Zeiger Größe oder größer, als Sie erwarten.std::function
hat eine min Größe implizit die standard - (Sie müssen in der Lage sein, zu speichern, "in sich selbst" callables bis zu einer bestimmten Größe), die mindestens so groß ist wie ein Funktionszeiger in der Praxis).In der
int(*f)()
Fall, den Sie speichern einen Funktionszeiger auf eine Funktion, die sich so verhält, wie-wenn man angerufen, dass die lambda. Dies funktioniert nur für zustandslose lambdas (mit einer leeren[]
capture-Liste).In der
std::function<int()> f
Fall, erstellen Sie ein type-erasure-Klassestd::function<int()>
Instanz (in diesem Fall) verwendet, die Platzierung neuer zum speichern einer Kopie der Größe-1 lambda in einem internen Puffer (und, wenn ein größeres lambda-Ausdruck übergeben wurde (mit mehr Staat) nutzen würde, heap allocation).Als eine Vermutung, so etwas wie dies ist wahrscheinlich das, was Sie denken, ist passiert. Das lambda ist ein Objekt, dessen Typ wird beschrieben durch Ihre Signatur. In C++, es war entschieden, um lambdas null-Kosten Abstraktionen über die manuelle Funktion-Objekt-Implementierung. Dadurch können Sie an einem lambda-Ausdruck in eine
std
Algorithmus (oder ähnlich) und haben seinen Inhalt vollständig sichtbar, der compiler, wenn es instanziiert wird, der Algorithmus Vorlage. Wenn eine lambda hatte einen Typ wiestd::function<void(int)>
, der Inhalt würde nicht vollständig sichtbar ist, und einem handgefertigten Objekt Funktion könnte schneller sein.Dem Ziel der C++ - Standardisierung ist die high-level-Programmierung mit null-overhead-über-hand-crafted C-code.
Nun, dass Sie verstehen, dass Ihre
f
ist in der Tat staatenlos, sollte es eine weitere Frage in Ihrem Kopf: der lambda-Ausdruck hat keinen Staat. Warum macht es nicht die Größe haben0
?Es ist die kurze Antwort.
Alle Objekte in C++ muss ein minimium Größe von 1, unter dem standard, und zwei Objekte den gleichen Typ haben, kann nicht die gleiche Adresse. Diese sind miteinander verbunden, weil ein array vom Typ
T
werden die Elemente platziertsizeof(T)
auseinander.Nun, da es keine Staatliche, manchmal kann es dauern, bis keinen Platz. Dies kann nicht passieren, wenn es "allein", aber in manchen Kontexten kann es passieren.
std::tuple
und ähnliche code-Bibliothek nutzt diese Tatsache. Hier ist, wie es funktioniert:Als ein lambda-Ausdruck ist äquivalent zu einer Klasse mit
operator()
überlastet, zustandslose lambdas (mit einem[]
capture-Liste) sind alle leeren Klassen. Sie habensizeof
von1
. In der Tat, wenn Sie Erben von Ihnen (was zulässig ist!), Sie nehmen keinen Platz so lange, wie es nicht zu einer gleichen-Typ-Adresse Kollision. (Dies ist bekannt als die leeren sockel-Optimierung).den
sizeof(make_toy( []{std::cout << "hello world!\n"; } ))
istsizeof(int)
(gut, das oben ist illegal, weil Sie können nicht erstellen Sie einen lambda-Ausdruck in eine nicht ausgewertet Zusammenhang: Sie haben zum erstellen einer benanntenauto toy = make_toy(blah);
dann tunsizeof(blah)
, aber das ist nur Lärm).sizeof([]{std::cout << "hello world!\n"; })
noch1
(vergleichbare Qualifikation).Wenn wir ein anderes Spielzeug Typ:
dieser hat zwei Kopien von lambda. Denn Sie können nicht die gleiche Adresse,
sizeof(toy2(some_lambda))
ist2
!()
Hinzugefügt.Lambda ist nicht eine Funktion Zeiger.
Lambda ist eine Instanz einer Klasse. Dein code ist ungefähr das äquivalent zu:
Die interne Klasse repräsentiert einen lambda-Ausdruck hat keinen Schüler, damit seine
sizeof()
1 ist (es kann nicht 0 sein, für die Gründe ausreichend dargelegt anderswo).Wenn Ihre lambda-waren zu erfassen einige Variablen, Sie werden als äquivalent zu class-Mitglieder, und Ihre
sizeof()
wird zeigen, entsprechend.sizeof()
kann nicht 0 sein?Dem compiler mehr oder weniger übersetzt, die den lambda-Ausdruck die folgende struct-Typ:
Da, die struct hat keine nicht-statischen Elemente, es hat die gleiche Größe wie eine leere Struktur, die
1
.Dass ändert sich, sobald Sie eine nicht-leere capture-Liste um deine lambda:
Die übersetzen
Da die erzeugte Struktur muss nun speichern Sie eine nicht-statische
int
Mitglied für die Erfassung, seine Größe zu wachsen, umsizeof(int)
. Die Größe wird weiter wachsen, wie Sie erfassen mehr Zeug.(Bitte nehmen Sie sich die Struktur-Analogie mit einem Körnchen Salz. Es ist zwar ein schöner Weg, sich Gedanken darüber zu machen, wie Lambda-Ausdrücke intern arbeiten, ist dies nicht eine wörtliche übersetzung von dem, was der compiler tun wird)
Nicht unbedingt. Nach dem standard, die Größe des einzigartigen, Unbenannte Klasse ist Implementierung-definiert. Auszug aus [expr.prim.lambda], C++, 14 (meine Hervorhebung):
In Ihrem Fall -- für den compiler, den Sie verwenden-Sie erhalten eine Größe von 1, das bedeutet nicht, es ist behoben. Es kann variieren zwischen verschiedenen compiler-Implementierungen.
Vom http://en.cppreference.com/w/cpp/language/lambda:
Vom http://en.cppreference.com/w/cpp/language/sizeof