Search code examples
c++language-lawyerdeclarationname-lookupelaborated-type-specifier

The first snippet below compiles, but the second doesn't. Why?


The snippet below compiles (demo):

struct A{ int i = 10; };

int main() {
    struct A{ int i = 20; };
    struct A;
    struct A a;
}

But this doesn't:

struct A{ int i = 10; };

int main() {
//    struct A{ int i = 20; };
    struct A;
    struct A a;
}

I can see that the answer is probably given by these paragraphs in the Standard:

[basic.lookup.elab]/2 and [basic.scope.pdecl]/7.

But I really don't know how to deduce the different behaviors shown above from these two paragraphs.

Note that in the first example the struct A is not first declared in the elaborated-type-specifier struct A;, but in the definition of struct A in main().

In the second example, the struct A is also not first declared in the elaborated-type-specifier struct A;, but in the definition of struct Ain global scope.


Solution

  • 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-seqopt identifier ;

    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.