c++ - programa - El primer fragmento a continuación se compila, pero el segundo no. ¿Por qué?
programa para compilar c++ (3)
Cada uno de los ejemplos contiene declaraciones de dos clases diferentes, ambas con el nombre A
Distingamos entre las clases cambiando el nombre de una de ellas a B
:
struct A{ int i = 10; };
int main() {
struct B{ int i = 20; };
struct B;
struct B b;
}
Lo anterior es semánticamente idéntico a su primer ejemplo. La clase A
nunca se usa.
struct A{ int i = 10; };
int main() {
struct B;
struct B b;
}
Esto es semánticamente idéntico a su segundo ejemplo. Está intentando crear un objeto de un tipo incompleto, la clase B
declarada hacia adelante.
Cambiar el nombre de B
nuevo a A
no cambia nada porque entonces la declaración de A
en las sombras main
la declaración de la otra A
a nivel global.
[basic.lookup.elab] / 2
Si el especificador de tipo elaborado no tiene un especificador de nombre anidado , y si el especificador de tipo elaborado aparece en una declaración con el formulario:
class-key
attribute-specifier-seq
identifier
opt;
El especificador de tipo elaborado es una declaración que introduce el nombre de clase como se describe en [basic.scope.pdecl].
Así que la struct A;
Es una declaración que introduce el nombre de la clase en el alcance de la declaración. Bajo ninguna circunstancia puede referirse a una clase declarada en un ámbito externo.
[basic.scope.pdecl] / 7
[ Nota: otras formas de especificador de tipo elaborado no declaran un nuevo nombre [...] - nota final ]
Por implicación, esta forma de especificador de tipo elaborado declara un nuevo nombre.
El siguiente fragmento compila ( demo ):
struct A{ int i = 10; };
int main() {
struct A{ int i = 20; };
struct A;
struct A a;
}
Pero esto no lo hace:
struct A{ int i = 10; };
int main() {
// struct A{ int i = 20; };
struct A;
struct A a;
}
Puedo ver que la respuesta está probablemente dada por estos párrafos en la Norma:
[basic.lookup.elab]/2 y [basic.scope.pdecl]/7 .
Pero realmente no sé cómo deducir los diferentes comportamientos que se muestran arriba de estos dos párrafos.
Tenga en cuenta que en el primer ejemplo, la struct A
no se declara primero en la struct A;
especificación de tipo elaborada struct A;
, pero en la definición de struct A
en main()
.
En el segundo ejemplo, la struct A
tampoco se declara primero en la struct A;
especificación de tipo elaborada struct A;
, pero en la definición de struct A
en ámbito global.
En el segundo ejemplo, la línea struct A;
es una declaración hacia adelante para una estructura llamada A en el ámbito de la función principal. Esta estructura será preferida a la struct A
global struct A
La siguiente línea define una variable llamada de tipo struct A
Como se declaró una struct A
en el ámbito de la función principal, ahí es donde el compilador buscará su definición allí. No encuentra uno (está comentado). El primer ejemplo compila porque hay definición en el mismo ámbito. Sin embargo, el siguiente ejemplo se compilará porque especificó que A
está en el espacio de nombres global:
struct A{ int i = 10; };
int main() {
// struct A{ int i = 20; };
struct A;
struct ::A a;
}
No compila porque no puede encontrar una definición para A.
int main() {
// struct A{ int i = 20; };
struct A;
struct A a;
}
El código anterior es igual a su primer ejemplo, ya que la A global está sombreada por la A. local. En el segundo ejemplo, A no tiene una definición. Es solo un prototipo. Se supone que los prototipos se colocan antes de un fragmento de código que necesita una definición cuando se coloca DESPUÉS del código que lo necesita. Si el compilador no puede encontrar esa definición, fallará porque no sabe qué se supone que es A (la definición global está sombreada por el prototipo local, lo que hace que se ignore).