Warum ist keine std::initializer_list
Kernsprache eingebaut?
Es scheint mir, dass es ein ziemlich wichtiges Feature von C ++ 11 ist und dennoch kein eigenes reserviertes Schlüsselwort (oder ähnliches) hat.
Stattdessen handelt initializer_list
es sich nur um eine Vorlagenklasse aus der Standardbibliothek, die über eine spezielle implizite Zuordnung aus der neuen Syntax der Klammer-Init-Liste verfügt {...}
, die vom Compiler verarbeitet wird.
Auf den ersten Blick ist diese Lösung ziemlich hackig .
Werden auf diese Weise neue Ergänzungen zur C ++ - Sprache implementiert: durch implizite Rollen einiger Vorlagenklassen und nicht durch die Kernsprache ?
Bitte beachten Sie diese Beispiele:
widget<int> w = {1,2,3}; //this is how we want to use a class
Warum wurde eine neue Klasse gewählt:
widget( std::initializer_list<T> init )
anstatt etwas Ähnliches wie eine dieser Ideen zu verwenden:
widget( T[] init, int length ) // (1)
widget( T... init ) // (2)
widget( std::vector<T> init ) // (3)
- Ein klassisches Array, das Sie wahrscheinlich
const
hier und da hinzufügen könnten - drei Punkte sind bereits in der Sprache (var-args, jetzt variadische Vorlagen), warum nicht wieder verwendet die Syntax (und macht sie fühlen Built-in )
- nur ein vorhandener Container, könnte hinzufügen
const
und&
Alle von ihnen sind bereits ein Teil der Sprache. Ich habe nur meine 3 ersten Ideen geschrieben, ich bin mir sicher, dass es viele andere Ansätze gibt.
quelle
std::array<T>
ist nicht mehr "Teil der Sprache" alsstd::initializer_list<T>
. Und dies sind bei weitem nicht die einzigen Bibliothekskomponenten, auf die sich die Sprache stützt. Seenew
/delete
,type_info
verschiedene Ausnahmetypen,size_t
etc.const T(*)[N]
, denn das verhält sich sehr ähnlich wie esstd::initializer_list
funktioniert.std::array
oder ein Array mit statischer Größe weniger wünschenswerte Alternativen sind.Antworten:
Es gab bereits Beispiele für "Kern" -Sprachenfunktionen, die im
std
Namespace definierte Typen zurückgaben .typeid
kehrt zurückstd::type_info
und (vielleicht einen Punkt strecken)sizeof
kehrt zurückstd::size_t
.Im ersteren Fall müssen Sie bereits einen Standardheader einfügen, um diese sogenannte "Kernsprache" -Funktion nutzen zu können.
Bei Initialisierungslisten wird zum Generieren des Objekts kein Schlüsselwort benötigt. Die Syntax besteht aus kontextsensitiven geschweiften Klammern. Abgesehen davon ist es das gleiche wie
type_info
. Persönlich glaube ich nicht, dass das Fehlen eines Schlüsselworts es "hackiger" macht. Vielleicht etwas überraschender, aber denken Sie daran, dass das Ziel darin bestand, die gleiche Syntax für Klammerinitialisierer zuzulassen, die bereits für Aggregate zulässig war.Ja, Sie können wahrscheinlich in Zukunft mehr von diesem Designprinzip erwarten:
std
nicht als integrierte Typen, sondern als solche eingefügt.Daher:
std
.Ich denke, es kommt darauf an, dass es in C ++ keine absolute Trennung zwischen der "Kernsprache" und den Standardbibliotheken gibt. Es sind verschiedene Kapitel im Standard, aber jedes verweist auf das andere, und das war schon immer so.
In C ++ 11 gibt es einen anderen Ansatz: Lambdas führen Objekte ein, deren anonyme Typen vom Compiler generiert wurden. Da sie keine Namen haben, befinden sie sich überhaupt nicht in einem Namespace, schon gar nicht in
std
. Dies ist jedoch kein geeigneter Ansatz für Initialisierungslisten, da Sie den Typnamen verwenden, wenn Sie den Konstruktor schreiben, der einen akzeptiert.quelle
type_info
undsize_t
sind nette Argumente ..size_t
naja ist nur ein typedef .. also lasst uns das überspringen. Der Unterschied zwischentype_info
undinitializer_list
besteht darin, dass der erste das Ergebnis eines expliziten Operators und der zweite das Ergebnis einer impliziten Compileraktion ist. Es scheint mir auch, dassinitializer_list
könnte mit einigen bereits vorhandenen Containern .. oder noch besser ersetzt werden: jeder der Nutzer erklärt als Argument Typ!vector
,array
einen Vektor aus einem beliebigen Array des richtigen Typs erstellen können , nicht nur aus einem, das durch die Syntax der Initialisiererliste generiert wird. Ich bin mir nicht sicher, ob es eine schlechte Sache wäre, Container aus irgendwelchen zu konstruierenarray
, aber es ist nicht die Absicht des Komitees, die neue Syntax einzuführen.std::array
hat nicht einmal Konstruktoren. Derstd::array
Fall ist einfach eine Aggregatinitialisierung. Außerdem begrüße ich Sie, sich mir im Chatraum der Lounge <C ++> anzuschließen, da diese Diskussion etwas lang wird.Das C ++ Standard Committee scheint es vorzuziehen, keine neuen Schlüsselwörter hinzuzufügen, wahrscheinlich weil dies das Risiko erhöht, vorhandenen Code zu beschädigen (Legacy-Code könnte dieses Schlüsselwort als Namen einer Variablen, einer Klasse oder was auch immer verwenden).
Darüber hinaus scheint mir die Definition
std::initializer_list
als Vorlagencontainer eine recht elegante Wahl zu sein: Wenn es sich um ein Schlüsselwort handeln würde, wie würden Sie auf den zugrunde liegenden Typ zugreifen? Wie würden Sie es durchlaufen? Sie würden auch eine Reihe neuer Operatoren benötigen, und das würde Sie nur dazu zwingen, sich mehr Namen und mehr Schlüsselwörter zu merken, um die gleichen Dinge zu tun, die Sie mit Standardcontainern tun können.Wenn Sie einen
std::initializer_list
wie jeden anderen Container behandeln, haben Sie die Möglichkeit, generischen Code zu schreiben, der mit all diesen Dingen funktioniert.AKTUALISIEREN:
Zunächst verfügen alle anderen Container über Methoden zum Hinzufügen, Entfernen und Einfügen von Elementen, die für eine vom Compiler generierte Sammlung nicht wünschenswert sind. Die einzige Ausnahme ist
std::array<>
, dass ein Array im C-Stil mit fester Größe eingeschlossen wird und daher der einzig vernünftige Kandidat bleibt.Wie Nicol Bolas in den Kommentaren zutreffend hervorhebt , besteht ein weiterer grundlegender Unterschied zwischen
std::initializer_list
und allen anderen Standardcontainern (einschließlichstd::array<>
) darin, dass letztere eine Wertesemantik undstd::initializer_list
eine Referenzsemantik aufweisen . Das Kopierenstd::initializer_list
von beispielsweise führt nicht zu einer Kopie der darin enthaltenen Elemente.Darüber hinaus (erneut mit freundlicher Genehmigung von Nicol Bolas) ermöglicht ein spezieller Container für Listen zur Klammerinitialisierung eine Überlastung der Art und Weise, wie der Benutzer die Initialisierung durchführt.
quelle
std::array
. Aberstd::array
zuordnet Speicher währendstd::initializaer_list
Wraps ein Compiler-Array. Betrachten Sie es als den Unterschied zwischenchar s[] = "array";
undchar *s = "initializer_list";
.std::array
reserviert keinen Speicher, es ist eine einfacheT arr[N];
Sache, das gleiche, was unterstützt wirdstd::initializer_list
.T arr[N]
reserviert Speicher, möglicherweise nicht im dynamischen Heap, sondern anderswo ... So auchstd::array
. Ein Nicht-Leerzeicheninitializer_list
kann jedoch nicht vom Benutzer erstellt werden, sodass offensichtlich kein Speicher zugewiesen werden kann.Das ist nichts Neues. Beruht sich beispielsweise
for (i : some_container)
auf das Vorhandensein bestimmter Methoden oder eigenständiger Funktionen in dersome_container
Klasse. C # verlässt sich noch mehr auf seine .NET-Bibliotheken. Eigentlich denke ich, dass dies eine ziemlich elegante Lösung ist, da Sie Ihre Klassen mit einigen Sprachstrukturen kompatibel machen können, ohne die Sprachspezifikation zu komplizieren.quelle
begin
undend
Methoden. Das ist ein bisschen anders IMO.iterable class MyClass { };
initializer_list
wennDies ist in der Tat nichts Neues und wie viele darauf hingewiesen haben, war diese Praxis in C ++ vorhanden und beispielsweise in C #.
Andrei Alexandrescu hat jedoch einen guten Punkt erwähnt: Sie können sich das als Teil des imaginären "Kern" -Namensraums vorstellen, dann ist es sinnvoller.
Also, es ist tatsächlich so etwas wie:
core::initializer_list
,core::size_t
,core::begin()
,core::end()
und so weiter. Dies ist nur ein unglücklicher Zufall, dass derstd
Namespace einige Kernsprachenkonstrukte enthält.quelle
Es kann nicht nur vollständig in der Standardbibliothek funktionieren. Die Aufnahme in die Standardbibliothek bedeutet nicht, dass der Compiler keine cleveren Streiche spielen kann.
Obwohl dies möglicherweise nicht in allen Fällen möglich ist, kann es durchaus heißen: Dieser Typ ist bekannt oder ein einfacher Typ. Lassen Sie
initializer_list
uns das ignorieren und haben Sie nur ein Speicherbild von dem, was der initialisierte Wert sein sollte.Mit anderen Worten
int i {5};
kann äquivalent zuint i(5);
oderint i=5;
oder sogarintwrapper iw {5};
Wointwrapper
ist eine einfache Wrapper-Klasse über einem int, wobei ein trivialer Konstruktor ein nimmtinitializer_list
quelle
int i {5}
die irgendetwas beinhaltet,std::initializer_list
falsch.int
Da kein Konstruktor verwendet wirdstd::initializer_list
,5
wird der nur direkt zum Erstellen verwendet. Das Hauptbeispiel ist also irrelevant; Es ist einfach keine Optimierung durchzuführen. Dastd::initializer_list
der Compiler darüber hinaus ein "imaginäres" Array erstellt und ersetzt, kann dies meiner Meinung nach die Optimierung begünstigen, aber das ist der "magische" Teil des Compilers. Es ist also unabhängig davon, ob der Optimierer im Allgemeinen etwas Kluges mit dem Hübschen tun kann langweiliges Objekt mit 2 Iteratoren, die resultierenEs ist nicht Teil der Kernsprache, da es vollständig in der Bibliothek implementiert werden kann, nur Zeile
operator new
undoperator delete
. Welchen Vorteil hätte es, Compiler komplizierter zu machen?quelle