Mi intento de inicialización de valor se interpreta como una declaración de función, y ¿por qué no A a(()); ¿resuélvelo?

10 minutos de lectura

Mi intento de inicializacion de valor se interpreta como una
GRB

Entre las muchas cosas que Stack Overflow me ha enseñado está lo que se conoce como el “análisis más desconcertante”, 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 Atomando un temporal B objeto como un parámetro de constructor, en realidad es una declaración de una función a devolver un Atomando un puntero a una función que devuelve B y en sí mismo no toma parámetros. Del mismo modo la línea

A a(); //declares a function

también cae en 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 del 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 alternativa’ correcta es cambiarlo a A a;pero tengo curiosidad por saber qué es lo extra () hace para el compilador en el primer ejemplo que luego no funciona cuando se vuelve a aplicar en el segundo ejemplo. Es el A a((B())); ¿Solucionar 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.

    – Pavel Minaev

    15 de septiembre de 2009 a las 0:32

  • También hay que señalar que el segundo caso, A a(); es no de la misma categoría. Para el compiladornunca hay una forma diferente de analizarlo: un inicializador en ese lugar nunca consta de paréntesis vacíos, por lo que siempre es una declaración de función.

    – Johannes Schaub – litb

    15 de septiembre de 2009 a las 2:08

  • El punto excelente de litb es sutil pero importante y vale la pena enfatizarlo: la razón por la que existe la ambigüedad en esta declaración ‘A a(B())’ está en el análisis de ‘B()’ -> puede ser tanto una expresión como una declaración y el compilador debe ‘seleccionar’ decl sobre expr, por lo que si B() es un decl, entonces ‘a’ solo puede ser un decl func (no un decl variable). Si se permitiera que ‘()’ fuera un inicializador, ‘A a()’ sería ambiguo, pero no expr frente a decl, sino var decl frente a func decl; no existe una regla para preferir una decl sobre otra, por lo que ‘() ‘ simplemente no está permitido como inicializador aquí, y la ambigüedad no aumenta.

    – Faisal Vali

    15 de septiembre de 2009 a las 4:29

  • A a(); es no un ejemplo de la análisis más irritante. Es simplemente una declaración de función, tal como lo es en C.

    – Pete Becker

    27 de marzo de 2013 a las 11:32


  • “la ‘solución alternativa’ correcta es cambiarlo a A a;” está mal. Eso no le dará la inicialización de un tipo POD. Para obtener la inicialización, escriba A a{};.

    – Saludos y hth. – alf

    1 de septiembre de 2017 a las 17:33

1647649870 622 Mi intento de inicializacion de valor se interpreta como una
Brian R. Bondy

No hay una respuesta ilustrada, es solo porque el lenguaje C ++ no la define como una sintaxis válida … Entonces lo es, por definición del lenguaje.

Si tiene una expresión dentro, entonces es válida. Por ejemplo:

 ((0));//compiles

Dicho de manera aún más simple: porque (x) es una expresión C++ válida, mientras que () no es.

Para obtener más información sobre cómo se definen los lenguajes y cómo funcionan los compiladores, debe aprender sobre Teoría del lenguaje formal o más específicamente Gramáticas libres de contexto (CFG) y material relacionado como 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.

  • Dicho de manera aún más simple: porque (x) es una expresión C++ válida, mientras que () no es.

    – Pavel Minaev

    15 de septiembre de 2009 a las 0:31

  • He aceptado esta respuesta, además el comentario de Pavel a mi pregunta inicial me ayudó mucho

    – GRB

    15 de septiembre de 2009 a las 0:40

Mi intento de inicializacion de valor se interpreta como una
David

La solución final a este problema es pasar a la sintaxis de inicialización uniforme de C+11 si puede.

A a{};

http://www.stroustrup.com/C++11FAQ.html#uniform-init

Mi intento de inicializacion de valor se interpreta como una
Konrad Borowski

declaradores de funciones C

En primer lugar, está C. En C, A a() es 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 la función. Los nombres de los argumentos son opcionales en las declaraciones, así que los omití en este ejemplo.

int putchar(int);

Esto le permite escribir el código de esta manera.

int puts(const char *);
int main() {
    puts("Hello, world!");
}

C también le permite definir funciones que toman funciones como argumentos, con una sintaxis agradable y legible que parece una llamada de función (bueno, es legible, siempre que no devuelva un puntero a la función).

#include <stdio.h>

int eighty_four() {
    return 84;
}

int output_result(int callback()) {
    printf("Returned: %d\n", callback());
    return 0;
}

int main() {
    return output_result(eighty_four);
}

Como mencioné, C permite omitir nombres de argumentos en archivos de encabezado, por lo tanto, el output_result se vería así en el archivo de encabezado.

int output_result(int());

Un argumento en constructor

¿No reconoces a ese? Bueno, déjame recordarte.

A a(B());

Sí, es exactamente la misma declaración de función. A es int, a es output_resulty B es int.

Puede notar fácilmente un conflicto de C con las nuevas características de C++. Para ser exactos, los constructores son el nombre de la clase y los paréntesis, y la 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 funciones antiguas de C tienen prioridad sobre las nuevas funciones de C++. La gramática de 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 habría ocurrido para la sintaxis con un argumento.

Ahora puedes preguntar por qué A a((B())) obras. Bueno, vamos a declarar output_result con paréntesis inútiles.

int output_result((int()));

No funcionará. La gramática requiere que la variable no esté entre paréntesis.

<stdin>:1:19: error: expected declaration specifiers or ‘...’ before ‘(’ token

Sin embargo, C++ espera una expresión estándar aquí. En C++, puede escribir el siguiente código.

int value = int();

Y el siguiente código.

int value = ((((int()))));

C++ espera que la expresión dentro de los paréntesis sea… bueno… expresión, a diferencia del tipo que C 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 (que simplemente espera una expresión, como 2 + 2).

Más argumentos en el constructor.

Seguramente un argumento es bueno, pero ¿qué pasa con dos? No es que los constructores puedan tener un solo argumento. Una de las clases integradas que toma dos argumentos es std::string

std::string hundred_dots(100, '.');

Todo esto está muy bien (técnicamente, tendría un análisis más molesto 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.

std::string hundred_dots((100, '.'));

No del todo.

<stdin>:2:36: error: invalid conversion from ‘char’ to ‘const char*’ [-fpermissive]
In file included from /usr/include/c++/4.8/string:53:0,
                 from <stdin>:1:
/usr/include/c++/4.8/bits/basic_string.tcc:212:5: error:   initializing argument 1 of ‘std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ [-fpermissive]
     basic_string<_CharT, _Traits, _Alloc>::
     ^

No estoy seguro de por qué g ++ intenta convertir char para const char *. De cualquier manera, se llamó al constructor con solo un valor de tipo char. No hay sobrecarga que tenga un argumento de tipo char, por lo tanto, el compilador está confundido. Puede preguntar: ¿por qué el argumento es de tipo char?

(100, '.')

Sí, , aquí hay un operador de coma. El operador de coma toma dos argumentos y proporciona el argumento del lado derecho. No es realmente útil, pero es algo que debe saberse para mi explicación.

En cambio, para resolver el análisis más molesto, se necesita el siguiente código.

std::string hundred_dots((100), ('.'));

Los argumentos están entre paréntesis, no la expresión completa. De hecho, solo una de las expresiones debe estar entre paréntesis, ya que es suficiente romper ligeramente con la gramática C para usar la función C++. Things nos lleva al punto de cero argumentos.

Cero argumentos en el constructor.

Es posible que hayas notado la eighty_four función en mi explicación.

int eighty_four();

Sí, esto también se ve afectado por el análisis más molesto. Es una definición válida, y es muy probable que haya visto si creó archivos de encabezado (y debería hacerlo). Agregar paréntesis no lo soluciona.

int eighty_four(());

¿Por qué es así? Bien, () no es una expresión. En C++, debe poner una expresión entre paréntesis. No puedes escribir auto 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 C ++ 11 {} sintaxis, ya que no hay expresiones para poner entre paréntesis, y siempre se aplicará la gramática C para las declaraciones de funciones.

en su lugar podrías

A a(());

utilizar

A a=A();

Los paréntesis más internos en su ejemplo serían una expresión, y en C++ la gramática define un expression ser un assignment-expression u otro expression seguida de una coma y otra assignment-expression (Apéndice A.4 – Resumen gramatical/Expresiones).

La gramática define además un assignment-expression como uno de varios otros tipos de expresión, ninguno de los cuales puede ser nada (o solo espacios en blanco).

Así que la razón por la que no puedes 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 paréntesis vacíos como una especie de caso especial; supongo que preferirían no poner en un caso tan especial si hubiera una alternativa razonable.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad