¿Por qué C ++ nos permite rodear el nombre de la variable entre paréntesis al declarar una variable?

84

Por ejemplo, una declaración como esa:

int (x) = 0;

O incluso eso:

int (((x))) = 0;

Me encontré con esto porque en mi código tenía un fragmento similar al siguiente:

struct B
{
};

struct C
{
  C (B *) {}
  void f () {};
};

int main()
{
  B *y;
  C (y);
}

Obviamente, quería construir un objeto Cque luego haría algo útil en su destructor. Sin embargo, como sucede, el compilador la trata C (y);como una declaración de variable ycon tipo Cy, por lo tanto, imprime un error sobre la yredefinición. Lo interesante es que si lo escribo como C (y).f ()o como algo parecido C (static_cast<B*> (y)), se compilará según lo previsto. La mejor solución alternativa moderna es utilizar{} en la llamada al constructor, por supuesto.

Entonces, como descubrí después de eso, es posible declarar variables como int (x) = 0;o incluso, int (((x))) = 0;pero nunca he visto a nadie usar declaraciones como esta. Así que estoy interesado, ¿cuál es el propósito de tal posibilidad porque por ahora veo que solo crea el caso similar al notorio "análisis más irritante" y no agrega nada útil?

Predelnik
fuente
El "propósito" de la posibilidad es probablemente simplificar el analizador.
molbdnilo
1
@GSerg Es curioso cómo el texto de mi pregunta responde a la pregunta de la segunda respuesta en su pregunta vinculada, ya que proporciono el ejemplo en el que permitir tales declaraciones conduce a resultados inesperados :)
Predelnik
entró en esta trampa: no sabía que el mutex estaba comentado y luego accidentalmente escribió la declaración incorrecta entre paréntesis. // std :: mutex std :: bloqueo_unico <std :: mutex> (m_mutex);
Sirve a Laurijssen el

Respuestas:

80

Agrupamiento.

Como ejemplo particular, considere que puede declarar una variable de tipo de función como

int f(int);

Ahora, ¿cómo declararías un puntero a algo así?

int *f(int);

¡No, no funciona! Esto se interpreta como una función que regresa int*. Debe agregar los paréntesis para que se analice de la manera correcta:

int (*f)(int);

El mismo trato con las matrices:

int *x[5];   // array of five int*
int (*x)[5]; // pointer to array of five int

fuente
13
Y para completar la respuesta: rechazar el caso particular sobre el que pregunta el autor de la pregunta requeriría una regla de caso especial adicional. La definición actual de cómo ()funciona un tipo es uniforme en todo el tipo.
Joseph Mansfield
Así que el caso especial se aplica al análisis más molesto. Esto se debe a que la sintaxis para inicializar variables con argumentos de constructor se agregó más tarde (¿con prisa, supongo?).
AnArrayOfFunctions
1
@FISOCPP Bueno. . si. C ++ vino después de C.. .
iheanyi
¿Esta respuesta se aplica igualmente a C, no solo a C ++?
kdbanman
" variable de tipo de función " ¡¿qué? !!
curioso
17

Por lo general, se permite el uso de paréntesis en tales declaraciones porque la declaración, desde el punto de vista sintáctico, siempre se ve así:

<front type> <specification>;

Por ejemplo, en la siguiente declaración:

int* p[2];

El "tipo frontal" es int (no int*) y la "especificación" es * p[2].

La regla es que puede usar cualquier número de paréntesis según sea necesario en la parte de "especificación" porque a veces es inevitable eliminar la ambigüedad. Por ejemplo:

int* p[2]; // array of 2 pointers to int; same as int (*p[2]);
int (*p)[2]; // pointer to an array of 2 ints

El puntero a una matriz es un caso raro, sin embargo, la misma situación que tiene con un puntero para funcionar:

int (*func(int)); // declares a function returning int*
int (*func)(int); // declares a pointer to function returning int

Esta es la respuesta directa a tu pregunta. Si su pregunta es sobre la declaración como C(y), entonces:

  • Ponga entre paréntesis toda la expresión (C(y))y obtendrá lo que quería
  • Esta declaración no hace más que crear un objeto temporal, que deja de vivir después de que finaliza esta instrucción (espero que esto sea lo que pretendías hacer).
Ethouris
fuente
1
Como mencioné, inicialmente estaba haciendo algo en el destructor, supongo que es algo bastante estándar cuando tienes algunas funciones de "encadenamiento" para establecer algunos parámetros y luego hacer todo el trabajo en el destructor. Sin embargo, gracias por una solución alternativa, supongo que escribir {}es la más válida después de todo.
Predelnik
4
trate de evitar inventar su propia gramática y utilice la que se proporciona en el estándar. <front-type> <specification>es engañoso y erróneo. La gramática es<declaration-specifier> <declarator>
Steve Cox
Tienes razón, no miré el estándar, solo repetí la regla de mi cabeza. En realidad, en C ++ 11, el papel de <declaration-specifier>también se puede jugar por autopalabra clave, por lo que ni siquiera siempre es un tipo.
Ethouris
@Pravasi Meet: Si ha editado la parte de mi publicación y ha cambiado los nombres en el esquema de sintaxis dado, cambie los nombres 3 líneas a continuación en consecuencia también. De lo contrario, todavía hay nombres antiguos "tipo frontal" y "especificación" y, por lo tanto, la publicación no tiene ningún sentido.
Ethouris