The first snippet below compiles, but the second doesn't. Why?
Each of the examples contains declarations of two different classes, both with the name A
.
Let's distinguish between the classes by renaming one of them to B
:
struct A{ int i = 10; };
int main() {
struct B{ int i = 20; };
struct B;
struct B b;
}
The above is semantically identical to your first example. The class A
is never used.
struct A{ int i = 10; };
int main() {
struct B;
struct B b;
}
This is semantically identical to your second example. You are trying to create an object of an incomplete type, the forward-declared class B
.
Renaming B
back to A
doesn't change anything because then the declaration of A
in main
shadows the declaration of the other A
at global scope.
[basic.lookup.elab]/2
If the elaborated-type-specifier has no nested-name-specifier, and [...] if the elaborated-type-specifier appears in a declaration with the form:
class-key
attribute-specifier-seq
optidentifier
;
the elaborated-type-specifier is a declaration that introduces the class-name as described in [basic.scope.pdecl].
So struct A;
is a declaration that introduces the class name in the scope of the declaration. Under no circumstances can it refer to a class declared in an outer scope.
[basic.scope.pdecl]/7
[ Note: Other forms of elaborated-type-specifier do not declare a new name [...] — end note ]
By implication, this form of elaborated-type-specifier declares a new name.
In the second example the line struct A;
is a forward declaration for a struct called A in the main function's scope. This struct will be preferred to the global struct A
. The next line defines a variable called a
of type struct A
. Since a struct A
was declared in the main function's scope, that's where the compiler will search for it's definition there. It fails to find one (it's commented out). The first example compiles because there is definition in the same scope. The following example will compile however because it specified that A
is in the global namespace :
struct A{ int i = 10; };
int main() {
// struct A{ int i = 20; };
struct A;
struct ::A a;
}
It doesn't compile because it can't find a definition for A.
int main() {
// struct A{ int i = 20; };
struct A;
struct A a;
}
The code above is equal to your first example, as the global A is shadowed by the local A. In the second example A doesn't have a definition. It's just a prototype. Prototypes are supposed to be placed before a piece of code that needs a definition when the definition is placed AFTER the code which needs it. If the compliler cannot find that definition it will fail because it doesn't know what A is supposed to be (the global definition is shadowed by the local prototype, which causes it to be ignored).