vectors strings sort array c++ syntax grammar language-lawyer

strings - vector of vector c++



int a[]={1,2,}; Se permite coma extraña. ¿Alguna razón en particular? (19)

Además de la generación de código y la facilidad de edición, si desea implementar un analizador, este tipo de gramática es más simple y más fácil de implementar. C # sigue esta regla en varios lugares donde hay una lista de elementos separados por comas, como elementos en una definición de enum .

Tal vez no soy de este planeta, pero me parece que lo siguiente debería ser un error de sintaxis:

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

Pero no lo es. Me sorprendió cuando este código se compiló en Visual Studio, pero aprendí a no confiar en el compilador de MSVC en lo que respecta a las reglas de C ++, por lo que verifiqué el estándar y el estándar también lo permite. Puedes ver 8.5.1 para las reglas gramaticales si no me crees.

¿Por qué se permite esto? Esta puede ser una pregunta estúpida e inútil, pero quiero que entiendas por qué lo pregunto. Si se tratara de un subcaso de una regla de gramática general, lo entendería: decidieron no hacer que la gramática general fuera más difícil simplemente para rechazar una coma redundante al final de una lista de inicializadores. Pero no, la coma adicional está explícitamente permitida. Por ejemplo, no se permite tener una coma redundante al final de una lista de argumentos de llamada de función (cuando la función toma ... ), lo cual es normal .

Entonces, de nuevo, ¿hay alguna razón en particular que se permita explícitamente esta coma redundante?


Como muchas otras cosas, la coma final en un inicializador de matriz es una de las cosas que C ++ heredó de C (y tendrá que admitir para siempre). Una visión totalmente diferente de las que se colocan aquí se menciona en el libro "Secretos de Deep C" .

En esto, después de un ejemplo con más de una "paradoja de coma":

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? */ };

leemos :

... esa coma final después del inicializador final no es un error tipográfico, sino una interferencia en la sintaxis que se transmite desde C original . Su presencia o ausencia está permitida pero no tiene importancia . La justificación alegada en la justificación ANSI C es que hace que la generación automatizada de C sea más fácil. La reclamación sería más creíble si se permitieran comas finales en cada lista separada por comas , como en declaraciones de enumeración, o declaradores de múltiples variables en una sola declaración. Ellos no son.

... para mí esto tiene más sentido


Creo que la coma final está permitida por razones de compatibilidad hacia atrás. Hay una gran cantidad de código existente, principalmente generado automáticamente, que pone una coma al final. Facilita la escritura de un bucle sin una condición especial al final. p.ej

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

Realmente no hay ninguna ventaja para el programador.

PD: Aunque es más fácil generar automáticamente el código de esta manera, en realidad siempre me cuidé de no poner la coma al final, los esfuerzos son mínimos, la legibilidad mejora y eso es más importante. Escribes código una vez, lo lees muchas veces.


El único idioma en el que no está permitido, en la práctica *, es Javascript, y causa una cantidad innumerable de problemas. Por ejemplo, si copia y pega una línea desde el centro de la matriz, la pega al final y olvida eliminar la coma, su sitio quedará totalmente dañado para sus visitantes de IE.

* En teoría, está permitido pero Internet Explorer no sigue el estándar y lo trata como un error


Es útil si haces algo como esto:

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


Es más fácil para las máquinas, es decir, análisis y generación de código. También es más fácil para los seres humanos, es decir, modificación, comentario y elegancia visual a través de la coherencia.

Asumiendo C, ¿escribirías lo siguiente?

#include <stdio.h> #include <stdlib.h> int main(void) { puts("Line 1"); puts("Line 2"); puts("Line 3"); return EXIT_SUCCESS }

No. No solo porque la declaración final es un error, sino también porque es inconsistente. Entonces, ¿por qué hacer lo mismo con las colecciones? Incluso en los idiomas que le permiten omitir los últimos puntos y comas y comas, a la comunidad generalmente no le gusta. A la comunidad de Perl, por ejemplo, no parece que le guste omitir los puntos y comas, ni las frases de una línea. Aplican eso a las comas también.

No omita comas en las colecciones multilínea por la misma razón por la que no omite los puntos y coma para los bloques de código multilínea. Quiero decir, no lo harías aunque el idioma lo permitiera, ¿verdad? ¿Derecha?


Esto se permite proteger de los errores causados ​​por mover elementos en una lista larga.

Por ejemplo, supongamos que tenemos un código con este aspecto.

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

Y es genial, ya que muestra la trilogía original de los sitios de Stack Exchange.

Super User Server Fault

Pero hay un problema con eso. Verá, el pie de página en este sitio web muestra Falla del servidor antes del superusuario. Mejor arreglar eso antes de que nadie se dé cuenta.

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

Después de todo, mover líneas no podría ser tan difícil, ¿verdad?

Server FaultSuper User

Lo sé, no hay un sitio web llamado "Servidor FaultSuper User", pero nuestro compilador afirma que existe. Ahora, el problema es que C tiene una función de concatenación de cadenas, que le permite escribir dos cadenas entre comillas dobles y concatenarlas sin usar nada (un problema similar también puede ocurrir con números enteros, ya que sign tiene múltiples significados).

Ahora, ¿qué pasaría si la matriz original tuviera una coma inútil al final? Bueno, las líneas se moverían, pero tal error no hubiera ocurrido. Es fácil pasar por alto algo tan pequeño como una coma. Si recuerdas poner una coma después de cada elemento del arreglo, tal error simplemente no puede ocurrir. No querrá perder cuatro horas depurando algo, hasta que encuentre que la coma es la causa de sus problemas .


Estoy sorprendido después de todo este tiempo, nadie ha citado el Manual de referencia anotado de C ++ ( ARM ), dice lo siguiente acerca de [dcl.init] con énfasis mío:

Claramente hay demasiadas anotaciones para las inicializaciones, pero cada una parece servir bien a un estilo particular de uso. La notación = {initializer_list, opt} fue heredada de C y sirve bien para la inicialización de estructuras de datos y matrices. [...]

aunque la gramática ha evolucionado desde que se escribió ARM, el origen permanece.

y podemos ir a la justificación de C99 para ver por qué se permitió esto en C y dice:

K&R permite una coma final en un inicializador al final de una lista de inicializadores. El Estándar ha conservado esta sintaxis, ya que proporciona flexibilidad para agregar o eliminar miembros de una lista de inicializadores y simplifica la generación de máquinas de dichas listas.


Facilidad de uso para el desarrollador, creo.

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

Además, si por alguna razón usted tuviera una herramienta que generara código para usted; la herramienta no tiene que preocuparse de si es el último elemento en la inicialización o no.


Facilita la generación de código fuente y también la escritura de código que puede extenderse fácilmente en una fecha posterior. Considere lo que se requiere para agregar una entrada adicional a:

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

... tienes que agregar la coma a la línea existente y agregar una nueva línea. Compare eso con el caso en el que los tres ya tienen una coma después de eso, donde solo tiene que agregar una línea. Del mismo modo, si desea eliminar una línea, puede hacerlo sin preocuparse de si es la última línea o no, y puede reordenar las líneas sin tener que usar comas. Básicamente significa que hay una uniformidad en cómo tratas las líneas.

Ahora piensa en generar código. Algo como (pseudo-código):

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

No hay que preocuparse por si el elemento actual que está escribiendo es el primero o el último. Mucho más sencillo.


Facilita la generación de código ya que solo necesita agregar una línea y no necesita tratar la adición de la última entrada como si fuera un caso especial. Esto es especialmente cierto cuando se usan macros para generar código. Hay un impulso para tratar de eliminar la necesidad de macros del lenguaje, pero gran parte del lenguaje evolucionó de la mano con macros disponibles. La coma adicional permite que se definan y utilicen macros como las siguientes:

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

Uso:

LIST_BEGIN LIST_ENTRY(1) LIST_ENTRY(2) LIST_END

Ese es un ejemplo muy simplificado, pero a menudo las macros utilizan este patrón para definir cosas como el envío, los mensajes, los eventos o los mapas y tablas de traducción. Si no se permitiera una coma al final, necesitaríamos un especial:

#define LIST_LAST_ENTRY(x) x

y eso sería muy incómodo de usar.


Hace que los generadores de código que escupen arrays o enumeraciones sean más fáciles.

Imagina:

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 decir, no es necesario realizar un manejo especial del primer o último elemento para evitar escupir la coma final.

Si el generador de código está escrito en Python, por ejemplo, es fácil evitar escupir la coma al usar la función str.join() :

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


La razón es trivial: la facilidad de agregar / eliminar líneas.

Imagina el siguiente código:

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

Ahora, puede agregar / eliminar elementos fácilmente a la lista sin tener que agregar / eliminar la coma al final.

A diferencia de otras respuestas, realmente no creo que la facilidad para generar la lista sea una razón válida: después de todo, es trivial para el código de la última línea (o la primera). Los generadores de código se escriben una vez y se usan muchas veces.


Permite que cada línea siga la misma forma. En primer lugar, esto hace que sea más fácil agregar nuevas filas y hacer que un sistema de control de versiones realice un seguimiento significativo del cambio y también le permita analizar el código más fácilmente. No puedo pensar en una razón técnica.


Si usa una matriz sin la longitud especificada, VC ++ 6.0 puede identificar automáticamente su longitud, por lo que si usa "int a [] = {1,2,};" la longitud de a es 3, pero la última no tiene '' t inicializado, puedes usar "cout <


Siempre he asumido que hace que sea más fácil agregar elementos adicionales:

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

simplemente se convierte en:

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

en una fecha posterior.


Todo lo que todos dicen acerca de la facilidad de agregar / eliminar / generar líneas es correcto, pero el lugar real en el que brilla esta sintaxis es cuando se combinan los archivos de origen. Imagina que tienes esta matriz:

int ints[] = { 3, 9 };

Y suponga que ha marcado este código en un repositorio.

Luego tu amigo lo edita, agregando al final:

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

Y simultáneamente lo editas, agregando al principio:

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

Semánticamente este tipo de operaciones (agregar al principio, agregar al final) debe ser completamente seguro y el software de versiones (con suerte git) debe poder automatizarse. Lamentablemente, este no es el caso porque su versión no tiene coma después del 9 y la de su amigo. Considerando que, si la versión original tuviera el 9 final, se habrían automatizado.

Por lo tanto, mi regla de oro es: use la coma al final si la lista abarca varias líneas, no la use si la lista está en una sola línea.


Una de las razones por las que esto está permitido es que debería ser simple generar código automáticamente; No necesitas ningún manejo especial para el último elemento.


Veo un caso de uso que no se mencionó en otras respuestas, nuestras macros favoritas:

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

Agregar macros para manejar los últimos , sería un gran dolor. Con este pequeño cambio en la sintaxis, esto es trivial de manejar. Y esto es más importante que el código generado por la máquina, ya que generalmente es mucho más fácil hacerlo en la versión completa de Turing que un preprocesador muy limitado.