int a = {1,2,}; Sonderkommando erlaubt. Irgend ein bestimmter Grund?

Vielleicht bin ich nicht von diesem Planeten, aber es scheint mir, dass das Folgende ein Syntaxerrors sein sollte:

int a[] = {1,2,}; //extra comma in the end 

Aber es ist nicht. Ich war überrascht, als dieser Code in Visual Studio kompiliert wurde, aber ich habe gelernt, dem MSVC-Compiler nicht zu vertrauen, soweit C ++ – Regeln betroffen sind, also überprüfte ich den Standard und es ist auch vom Standard erlaubt. Sie können 8.5.1 für die Grammatikregeln sehen, wenn Sie mir nicht glauben.

Bildbeschreibung hier eingeben

Warum ist das erlaubt? Das mag eine dumme unnütze Frage sein, aber ich möchte, dass Sie verstehen, warum ich frage. Wenn es sich um einen Unterfall einer allgemeinen Grammatikregel handeln würde, würde ich das verstehen – sie beschlossen, die allgemeine Grammatik nicht schwieriger zu machen, nur um ein überflüssiges Komma am Ende einer Initialisierungsliste zu verbieten. Aber nein, das zusätzliche Komma ist explizit erlaubt. Zum Beispiel ist es nicht erlaubt, am Ende einer functionsaufruf-Argumentliste ein redundantes Komma zu haben (wenn die function dauert ... ), was normal ist .

Gibt es also einen bestimmten Grund, warum dieses überflüssige Komma explizit erlaubt ist?

   

Es erleichtert das Generieren von Quellcode und das Schreiben von Code, der zu einem späteren Zeitpunkt einfach erweitert werden kann. Überlegen Sie, was erforderlich ist, um einen zusätzlichen Eintrag hinzuzufügen:

 int a[] = { 1, 2, 3 }; 

… müssen Sie das Komma zur bestehenden Zeile hinzufügen und eine neue Zeile hinzufügen. Vergleichen Sie das mit dem Fall, wo die drei bereits ein Komma haben, wo Sie nur eine Zeile hinzufügen müssen. Ebenso, wenn Sie eine Zeile entfernen möchten, können Sie dies tun, ohne sich darüber Gedanken zu machen, ob es die letzte Zeile ist oder nicht, und Sie können Zeilen neu anordnen, ohne mit Kommas herumzuspielen. Grundsätzlich bedeutet es, dass es eine Einheitlichkeit in der Behandlung der Linien gibt.

Denken Sie jetzt darüber nach, Code zu generieren. So etwas wie (Pseudocode):

 output("int a[] = {"); for (int i = 0; i < items.length; i++) { output("%s, ", items[i]); } output("};"); 

Sie müssen sich keine Gedanken darüber machen, ob der aktuelle Artikel, den Sie schreiben, der erste oder der letzte ist. Viel einfacher.

Es ist nützlich, wenn Sie so etwas tun:

 int a[] = { 1, 2, 3, //You can delete this line and it's still valid }; 

Benutzerfreundlichkeit für den Entwickler, würde ich denken.

 int a[] = { 1, 2, 2, 2, 2, 2, /*line I could comment out easily without having to remove the previous comma*/ } 

Außerdem, wenn Sie aus irgendeinem Grund ein Werkzeug haben, das Code für Sie generiert; Das Werkzeug muss sich nicht darum kümmern, ob es das letzte Element in der Initialisierung ist oder nicht.

Ich habe immer angenommen, dass es einfacher ist, zusätzliche Elemente hinzuzufügen:

 int a[] = { 5, 6, }; 

wird einfach:

 int a[] = { 5, 6, 7, }; 

zu einem späteren Zeitpunkt.

Alles, was jeder über die Leichtigkeit des Hinzufügens / Entfernens / Erzeugens von Zeilen sagt, ist korrekt, aber der wahre Ort, an dem diese Syntax glänzt, ist das Zusammenführen von Quelldateien. Stellen Sie sich vor, Sie haben dieses Array:

 int ints[] = { 3, 9 }; 

Und angenommen, Sie haben diesen Code in einem Repository überprüft.

Dann bearbeitet dein Kumpel es und fügt bis zum Ende hinzu:

 int ints[] = { 3, 9, 12 }; 

Und du redest es gleichzeitig und fügst zum Anfang hinzu:

 int ints[] = { 1, 3, 9 }; 

Semantisch sollten diese Arten von Operationen (Hinzufügen zum Anfang, Hinzufügen zum Ende) vollständig merge safe sein und Ihre Versions-Software (hoffentlich Git) sollte in der Lage sein, automatisch zu machen. Leider ist dies nicht der Fall, da deine Version nach der 9 kein Komma hat und dein Freund es tut. Wenn die ursprüngliche Version jedoch die nachgestellte 9 hätte, hätten sie automatisiert.

Also, meine Faustregel lautet: Verwenden Sie das nachfolgende Komma, wenn die Liste mehrere Zeilen umfasst, verwenden Sie es nicht, wenn sich die Liste in einer einzelnen Zeile befindet.

Trailing Komma Ich glaube, dass aus Gründen der Rückwärtskompatibilität erlaubt ist. Es gibt eine Menge vorhandenen Codes, der in erster Linie automatisch generiert wird und ein abschließendes Komma enthält. Es erleichtert das Schreiben einer Schleife ohne besondere Bedingung am Ende. z.B

 for_each(my_inits.begin(), my_inits.end(), [](const std::string& value) { std::cout < < value << ",\n"; }); 

Der Programmierer hat keinen wirklichen Vorteil.

PS Obwohl es einfacher ist, den Code auf diese Weise automatisch zu generieren, habe ich eigentlich immer darauf geachtet, das nachlaufende Komma nicht zu setzen, die Anstrengungen sind minimal, die Lesbarkeit ist verbessert, und das ist wichtiger. Du schreibst einmal Code, du liest es oft.

Einer der Gründe, warum dies so weit ich weiß, ist, dass es einfach sein sollte, automatisch Code zu generieren; Sie benötigen keine spezielle Handhabung für das letzte Element.

Es macht Code-Generatoren, die Arrays oder Aufzählungen leichter ausspucken.

Vorstellen:

 std::cout < < "enum Items {\n"; for(Items::iterator i(items.begin()), j(items.end); i != j; ++i) std::cout << *i << ",\n"; std::cout << "};\n"; 

Es ist also nicht nötig, den ersten oder letzten Punkt speziell zu behandeln, um zu vermeiden, dass das nachfolgende Komma ausgegeben wird.

Wenn der Code-Generator beispielsweise in Python geschrieben ist, ist es leicht zu vermeiden, das str.join() Komma mit der function str.join() zu spucken:

 print("enum Items {") print(",\n".join(items)) print("}") 

Die einzige Sprache, in der es – in der Praxis * – nicht erlaubt ist, ist Javascript, und es verursacht unzählige Probleme. Wenn Sie zum Beispiel eine Zeile aus der Mitte des Arrays kopieren und einfügen, sie am Ende einfügen und vergessen, das Komma zu entfernen, wird Ihre Site für Ihre IE-Besucher völlig zerstört.

* Theoretisch ist es erlaubt, aber Internet Explorer folgt nicht dem Standard und behandelt es als einen Fehler

Der Grund ist trivial: Einfaches Hinzufügen / Entfernen von Zeilen.

Stellen Sie sich den folgenden Code vor:

 int a[] = { 1, 2, //3, // - not needed any more }; 

Jetzt können Sie einfach Elemente zur Liste hinzufügen / entfernen, ohne dass Sie manchmal das nachgestellte Komma hinzufügen / entfernen müssen.

Im Gegensatz zu anderen Antworten glaube ich nicht, dass die Einfachheit der Erstellung der Liste ein triftiger Grund ist: Schließlich ist es für den Code trivial, die letzte (oder erste) Zeile als Sonderfall zu verwenden. Code-Generatoren werden einmal geschrieben und oft verwendet.

Es ist einfacher für Maschinen, dh das Parsen und Generieren von Code. Es ist auch einfacher für den Menschen, dh Veränderung, Kommentierung und visuelle Eleganz durch Konsistenz.

Angenommen, C, würden Sie folgendes schreiben?

 #include  #include  int main(void) { puts("Line 1"); puts("Line 2"); puts("Line 3"); return EXIT_SUCCESS } 

Nein. Nicht nur, weil die letzte Aussage ein Fehler ist, sondern auch, weil sie inkonsistent ist. Warum also dasselbe mit Sammlungen tun? Sogar in Sprachen, in denen Sie die letzten Semikolons und Kommas weglassen können, gefällt es der Community normalerweise nicht. Die Perl-Community scheint zum Beispiel keine Semikolons, Bar-Einzeiler, zu lassen. Sie wenden das auch auf Kommas an.

Lassen Sie Kommas in mehrzeiligen Sammlungen nicht aus dem gleichen Grund aus, wenn Sie Semikolons für mehrzeilige Code-Blöcke nicht weglassen. Ich meine, du würdest es nicht tun, selbst wenn die Sprache es erlaubte, oder? Recht?

Ich bin überrascht, nachdem die ganze Zeit niemand das Annotated C ++ Reference Manual ( ARM ) zitiert hat, es sagt folgendes über [dcl.init] mit der Betonung meins:

Es gibt eindeutig zu viele Notationen für Initialisierungen, aber jeder scheint einem bestimmten Verwendungsstil zu dienen. Die Schreibweise = {initializer_list, opt} wurde von C geerbt und dient zur Initialisierung von Datenstrukturen und Arrays. […]

Obwohl sich die Grammatik seit dem Schreiben von ARM weiterentwickelt hat , bleibt der Ursprung erhalten.

und wir können zu der C99-Begründung gehen, um zu sehen, warum dies in C erlaubt war und es heißt:

K & R ermöglicht ein abschließendes Komma in einem Initialisierer am Ende einer Initialisierungsliste. Der Standard hat diese Syntax beibehalten, da er beim Hinzufügen oder Löschen von Elementen aus einer Initialisierungsliste Flexibilität bietet und die maschinelle Erstellung solcher Listen vereinfacht.

Es erlaubt jeder Zeile die gleiche Form zu folgen. Dies erleichtert erstens das Hinzufügen neuer Zeilen und das Verfolgen eines Versionskontrollsystems durch ein Versionskontrollsystem und das leichtere Analysieren des Codes. Ich kann mir keinen technischen Grund vorstellen.

Dies darf vor Fehlern schützen, die durch das Verschieben von Elementen in einer langen Liste verursacht werden.

Angenommen, wir haben einen Code, der so aussieht.

 #include  #include  #include  #define ARRAY_SIZE(array) (sizeof(array) / sizeof *(array)) int main() { std::string messages[] = { "Stack Overflow", "Super User", "Server Fault" }; size_t i; for (i = 0; i < ARRAY_SIZE(messages); i++) { std::cout << messages[i] << std::endl; } } 

Und es ist großartig, da es die ursprüngliche Trilogie von Stack Exchange-Sites zeigt.

 Stack Overflow Super User Server Fault 

Aber es gibt ein Problem damit. Sie sehen, die Fußzeile auf dieser Website zeigt Server Fault vor Super User. Besser das beheben, bevor es jemand merkt.

 #include  #include  #include  #define ARRAY_SIZE(array) (sizeof(array) / sizeof *(array)) int main() { std::string messages[] = { "Stack Overflow", "Server Fault" "Super User", }; size_t i; for (i = 0; i < ARRAY_SIZE(messages); i++) { std::cout << messages[i] << std::endl; } } 

Immerhin, bewegte Linien herum könnten nicht so schwer sein, könnte es sein?

 Stack Overflow Server FaultSuper User 

Ich weiß, es gibt keine Website namens "Server FaultSuper User", aber unser Compiler behauptet, dass es existiert. Das Problem ist nun, dass C über eine String-Verkettungsfunktion verfügt, mit der Sie zwei Strings in doppelten Anführungszeichen schreiben und sie mit nichts verketten können (ein ähnliches Problem kann auch bei Ganzzahlen auftreten, da das -Zeichen mehrere Bedeutungen hat).

Was nun, wenn das ursprüngliche Array am Ende ein nutzloses Komma hatte? Nun, die Linien würden verschoben werden, aber ein solcher Fehler wäre nicht passiert. Es ist leicht, etwas so kleines wie ein Komma zu verpassen. Wenn Sie daran denken, nach jedem Array-Element ein Komma zu setzen, kann ein solcher Fehler einfach nicht passieren. Du solltest nicht vier Stunden damit verschwenden, etwas zu debuggen, bis du das Komma als Ursache für deine Probleme findest .

Ich sehe einen Anwendungsfall, der in anderen Antworten nicht erwähnt wurde, unsere Lieblingsmakros:

 int a [] = { #ifdef A 1, //this can be last if B and C is undefined #endif #ifdef B 2, #endif #ifdef C 3, #endif }; 

Das Hinzufügen von Makros, die zuletzt gehandhabt werden , wäre ein großer Schmerz. Mit dieser kleinen Änderung in der Syntax ist dies trivial zu verwalten. Und das ist wichtiger als maschinengenerierter Code, weil es normalerweise viel einfacher ist, es in Turing zu tun, als ein sehr begrenzter Preprocesor.

Wie viele andere Dinge ist das nachgestellte Komma in einem Array-Initialisierer eines der Dinge, die C ++ von C geerbt hat (und für immer unterstützen muss). Ein ganz anderer Blickwinkel als hier ist in dem Buch “Deep C secrets” erwähnt .

Darin ein Beispiel mit mehr als einem “Kommaparadoxen”:

 char *available_resources[] = { "color monitor" , "big disk" , "Cray" /* whoa! no comma! */ "on-line drawing routines", "mouse" , "keyboard" , "power cables" , /* and what's this extra comma? */ }; 

wir lesen :

… das nachstehende Komma nach dem letzten Initialisierer ist kein Tipperrors, sondern ein Blip in der Syntax, der vom Ureinwohner C übernommen wurde . Seine Anwesenheit oder Abwesenheit ist erlaubt, hat aber keine Bedeutung . Die in der ANSI C-Begründung beanspruchte Begründung ist, dass sie die automatisierte Erzeugung von C erleichtert. Der Anspruch wäre glaubwürdiger, wenn in jeder kommaseparierten Liste nachgestellte Kommas zulässig wären , z. B. in Enum-Deklarationen oder mehreren variablen Deklaratoren in einer einzelnen Deklaration. Sie sind nicht.

… für mich macht das mehr Sinn

Wenn Sie einen Parser implementieren möchten, ist diese Art der Grammatik einfacher und einfacher zu implementieren. C # folgt dieser Regel an mehreren Stellen, dass es eine Liste von durch Kommas getrennten Elementen gibt, z. B. Elemente in einer Definitionsdefinition.

Es macht das Generieren von Code einfacher, da Sie nur eine Zeile hinzufügen müssen und nicht das Hinzufügen des letzten Eintrags behandeln müssen, als wäre es ein Sonderfall. Dies gilt insbesondere, wenn Makros zum Generieren von Code verwendet werden. Es gibt einen Versuch, die Notwendigkeit von Makros aus der Sprache zu eliminieren, aber ein Großteil der Sprache entwickelte sich Hand in Hand mit verfügbaren Makros. Durch das zusätzliche Komma können Makros wie das Folgende definiert und verwendet werden:

 #define LIST_BEGIN int a[] = { #define LIST_ENTRY(x) x, #define LIST_END }; 

Verwendung:

 LIST_BEGIN LIST_ENTRY(1) LIST_ENTRY(2) LIST_END 

Das ist ein sehr vereinfachtes Beispiel, aber oft wird dieses Muster von Makros verwendet, um Dinge wie Versand-, Nachrichten-, Ereignis- oder Übersetzungskarten und -tabellen zu definieren. Wenn ein Komma am Ende nicht erlaubt war, brauchten wir ein spezielles:

 #define LIST_LAST_ENTRY(x) x 

und das wäre sehr umständlich zu benutzen.

Wenn Sie ein Array ohne angegebene Länge verwenden, kann VC ++ 6.0 seine Länge automatisch ermitteln. Wenn Sie also “int a [] = {1,2,};” verwenden, ist die Länge von a 3, die letzte jedoch nicht. Wenn Sie initialisiert haben, können Sie “cout <