A class name introduced inside a class is not treated as a nested class name

g++’s behavior here is entirely correct. This is specified in §3.3.2 [basic.scope.pdecl]/p7 of the standard:

The point of declaration of a class first declared in an
elaborated-type-specifier is as follows:

  • for a declaration of the form

    class-key attribute-specifier-seqopt identifier ;
    the identifier is declared to be a class-name in the scope that contains
    the declaration, otherwise
  • for an elaborated-type-specifier of the form
    class-key identifier
    if the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a
    function defined in namespace scope, the identifier is declared as a
    class-name in the namespace that contains the declaration; otherwise, except as a friend declaration*, the identifier is declared
    in the smallest namespace or block scope that contains the
    declaration.

Note that in the second case the declaration is always placed in a namespace or block scope, never a class scope, so it can never declare a nested class. Also, in the second case, a lookup will be performed and only if a previously declared type-name is not found will the elaborated-type-specifier be taken to declare a new name (§3.4.4 [basic.lookup.elab]/p2, §9.1 [class.name]/p3 note).


* Friend declarations have their own weird rules. The names first declared in friend declarations are still placed in namespace (for non-local classes) or block (for local classes) scope, but they are not made visible for most name lookup (except for ADL in case of functions) until they are also declared in the scope containing them. The rules for non-local classes are specified in §7.3.1.2 [namespace.memdef]/p3:

If a friend declaration in a non-local class first declares a class or function the friend class or function is a member of the innermost enclosing namespace. The name of the friend is not found by unqualified lookup (3.4.1) or by qualified lookup (3.4.3) until a matching declaration is provided in that namespace scope (either before or after the class definition granting friendship). If a friend function is called, its name may be found by the name lookup that considers functions from namespaces and classes associated with the types of the function arguments (3.4.2). If the
name in a friend declaration is neither qualified nor a template-id and the declaration is a function or an elaborated-type-specifier, the lookup to determine whether the entity has been previously declared shall not consider any scopes outside the innermost enclosing namespace.

The rules for local classes are specified in §11.3 [class.friend]/p11:

If a friend declaration appears in a local class (9.8) and the name specified is an unqualified name, a prior declaration is looked up without considering scopes that are outside the innermost enclosing non-class scope. […] For a friend class declaration, if there is no prior declaration, the class that is specified belongs to the innermost enclosing non-class scope, but if it is subsequently referenced, its name is not found by name lookup until a matching declaration is provided in the innermost enclosing non-class scope.

Leave a Comment