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.