Entre las muchas cosas que Stack Overflow me ha enseñado está lo que se conoce como el "análisis más irritante", que se demuestra clásicamente con una línea como
A a(B()); //declares a function
Si bien esto, para la mayoría, intuitivamente parece ser la declaración de un objeto a
de tipo A
, tomando un B
objeto temporal como un parámetro constructor, en realidad es una declaración de una función que a
devuelve un A
, tomando un puntero a una función que regresa B
y no toma parámetros . Del mismo modo la línea
A a(); //declares a function
También cae dentro de la misma categoría, ya que en lugar de un objeto, declara una función. Ahora, en el primer caso, la solución habitual para este problema es agregar un conjunto adicional de corchetes / paréntesis alrededor B()
, ya que el compilador lo interpretará como la declaración de un objeto
A a((B())); //declares an object
Sin embargo, en el segundo caso, hacer lo mismo conduce a un error de compilación
A a(()); //compile error
Mi pregunta es, ¿por qué? Sí, soy muy consciente de que la "solución" correcta es cambiarlo A a;
, pero tengo curiosidad por saber qué hace el extra ()
para el compilador en el primer ejemplo, que luego no funciona al volver a aplicarlo El segundo ejemplo. ¿La A a((B()));
solución es una excepción específica escrita en el estándar?
(B())
es solo una expresión de C ++, nada más. No es ningún tipo de excepción. La única diferencia que hace es que no hay forma de que pueda analizarse como un tipo, por lo que no lo es.A a();
es de la misma categoría. Para el compilador , nunca hay una forma diferente de analizarlo: un inicializador en ese lugar nunca consta de paréntesis vacíos, por lo que esta siempre es una declaración de función.A a();
No es un ejemplo del análisis más irritante . Es simplemente una declaración de función, al igual que en C.A a;
" está mal ". Eso no te dará la inicialización de un tipo de POD. Para obtener la inicialización escribaA a{};
.Respuestas:
No hay una respuesta ilustrada, es solo porque no está definida como sintaxis válida por el lenguaje C ++ ... Por lo tanto, es así, por definición del lenguaje.
Si tiene una expresión dentro, entonces es válida. Por ejemplo:
Aún más simple: porque
(x)
es una expresión válida de C ++, mientras()
que no lo es.Para obtener más información sobre cómo se definen los idiomas y cómo funcionan los compiladores, debe conocer la teoría del lenguaje formal o, más específicamente, las gramáticas libres de contexto (CFG) y el material relacionado, como las máquinas de estados finitos. Si está interesado en eso, aunque las páginas de Wikipedia no serán suficientes, tendrá que obtener un libro.
fuente
(x)
es una expresión válida de C ++, mientras()
que no lo es.La solución final a este problema es pasar a la sintaxis de inicialización uniforme C + 11 si es posible.
http://www.stroustrup.com/C++11FAQ.html#uniform-init
fuente
C declaradores de funciones
En primer lugar, hay C. En C,
A a()
es la declaración de función. Por ejemplo,putchar
tiene la siguiente declaración. Normalmente, tales declaraciones se almacenan en archivos de encabezado, sin embargo, nada le impide escribirlas manualmente, si sabe cómo se ve la declaración de función. Los nombres de los argumentos son opcionales en las declaraciones, por lo que lo omití en este ejemplo.Esto le permite escribir el código de esta manera.
C también le permite definir funciones que toman funciones como argumentos, con una buena sintaxis legible que parece una llamada a función (bueno, es legible, siempre que no devuelva un puntero a la función).
Como mencioné, C permite omitir nombres de argumentos en los archivos de encabezado, por lo tanto,
output_result
se vería así en el archivo de encabezado.Un argumento en constructor
¿No lo reconoces? Bueno, déjame recordarte.
Sí, es exactamente la misma declaración de función.
A
esint
,a
esoutput_result
yB
esint
.Puede notar fácilmente un conflicto de C con las nuevas características de C ++. Para ser exactos, los constructores son nombre de clase y paréntesis, y sintaxis de declaración alternativa con en
()
lugar de=
. Por diseño, C ++ intenta ser compatible con el código C y, por lo tanto, tiene que lidiar con este caso, incluso si prácticamente a nadie le importa. Por lo tanto, las características antiguas de C tienen prioridad sobre las nuevas características de C ++. La gramática de las declaraciones intenta hacer coincidir el nombre como función, antes de volver a la nueva sintaxis con()
si falla.Si una de esas características no existiera, o tuviera una sintaxis diferente (como
{}
en C ++ 11), este problema nunca hubiera ocurrido para la sintaxis con un argumento.Ahora puede preguntar por qué
A a((B()))
funciona. Bueno, declaremosoutput_result
con paréntesis inútiles.No va a funcionar La gramática requiere que la variable no esté entre paréntesis.
Sin embargo, C ++ espera una expresión estándar aquí. En C ++, puede escribir el siguiente código.
Y el siguiente código.
C ++ espera que la expresión dentro de paréntesis sea ... bueno ... expresión, a diferencia del tipo C que espera. Los paréntesis no significan nada aquí. Sin embargo, al insertar paréntesis inútiles, la declaración de la función C no coincide, y la nueva sintaxis puede coincidir correctamente (lo que simplemente espera una expresión, como
2 + 2
).Más argumentos en constructor
Seguramente un argumento es bueno, pero ¿y dos? No es que los constructores puedan tener un solo argumento. Una de las clases integradas que toma dos argumentos es
std::string
Todo esto está muy bien (técnicamente, sería más irritante si se escribiera como
std::string wat(int(), char())
, pero seamos honestos, ¿quién escribiría eso? Pero supongamos que este código tiene un problema molesto. Asumirías que tienes que poner todo entre paréntesis.No del todo.
No estoy seguro de por qué g ++ intentos para convertir
char
aconst char *
. De cualquier manera, se llamó al constructor con solo un valor de tipochar
. No hay sobrecarga que tenga un argumento de tipochar
, por lo tanto, el compilador está confundido. Usted puede preguntar: ¿por qué el argumento es de tipo char?Sí,
,
aquí hay un operador de coma. El operador de coma toma dos argumentos y da el argumento del lado derecho. No es realmente útil, pero es algo que se debe conocer por mi explicación.En cambio, para resolver el análisis más irritante, se necesita el siguiente código.
Los argumentos están entre paréntesis, no toda la expresión. De hecho, solo una de las expresiones debe estar entre paréntesis, ya que es suficiente para romper ligeramente la gramática de C para usar la función C ++. Las cosas nos llevan al punto de cero argumentos.
Cero argumentos en constructor
Es posible que haya notado la
eighty_four
función en mi explicación.Sí, esto también se ve afectado por el análisis más irritante. Es una definición válida, y una que probablemente haya visto si creó archivos de encabezado (y debería). Agregar paréntesis no lo arregla.
¿Por qué es así? Bueno,
()
no es una expresión. En C ++, debe poner una expresión entre paréntesis. No puede escribirauto value = ()
en C ++, porque()
no significa nada (e incluso si lo hiciera, como una tupla vacía (ver Python), sería un argumento, no cero). Prácticamente eso significa que no puede usar la sintaxis abreviada sin usar la sintaxis de C ++ 11{}
, ya que no hay expresiones para poner entre paréntesis, y la gramática de C para las declaraciones de funciones siempre se aplicará.fuente
Podrías en cambio
utilizar
fuente
int a = int();
se inicializaa
con 0,int a;
deja sina
inicializar. Una solución alternativa correcta es usar losA a = {};
agregados,A a;
cuando la inicialización predeterminada hace lo que desea, yA a = A();
en todos los demás casos, o simplemente usar de maneraA a = A();
consistente. En C ++ 11, solo useA a {};
Los paréntesis más internos en su ejemplo serían una expresión, y en C ++ la gramática define un
expression
para ser unoassignment-expression
u otroexpression
seguido de una coma y otroassignment-expression
(Apéndice A.4 - Resumen / Expresiones de gramática).La gramática define además
assignment-expression
como uno de varios otros tipos de expresión, ninguno de los cuales puede ser nada (o solo espacios en blanco).Entonces, la razón que no puede tener
A a(())
es simplemente porque la gramática no lo permite. Sin embargo, no puedo responder por qué las personas que crearon C ++ no permitieron este uso particular de parens vacías como algún tipo de caso especial; supongo que preferirían no poner un caso tan especial si hubiera Una alternativa razonable.fuente