c++language-lawyerc++14gcc6

Global scope resolution in the presence of using namespace


Consider the following code:

namespace foo {
  namespace bar {
    class foo {};
  }
  class baz {};
}
using namespace foo::bar;

::foo::baz mybaz;

Is this code valid? Or is ::foo ambiguous? Or does ::foo refer to the class foo, such that there is no ::foo::baz.

When it comes to compilers, gcc 6.1.1 seems to think the latter:

scope.cpp:9:8: error: ‘baz’ in ‘class foo::bar::foo’ does not name a type
 ::foo::baz mybaz;
        ^~~

On the other hand gcc 5.3.1, clang 3.8.0, and the intel compiler 16.0.3 do not yield any warnings or errors.

I suspect that under 3.4.3.2.2 of the C++14 standard, this should be valid and not ambiguous, but I am not quite sure.

Edit: In addition, for foo::baz mybaz only clang reports an ambiguous error.


Solution

  • [namespace.qual] is fairly straightforward:

    1 If the nested-name-specifier of a qualified-id nominates a namespace (including the case where the nested-name-specifier is ::, i.e., nominating the global namespace), the name specified after the nested-name-specifier is looked up in the scope of the namespace. The names in a template-argument of a template-id are looked up in the context in which the entire postfix-expression occurs.

    2 For a namespace X and name m, the namespace-qualified lookup set S(X,m) is defined as follows: Let S0(X,m) be the set of all declarations of m in X and the inline namespace set of X (7.3.1). If S0(X,m) is not empty, S(X,m) is S0(X,m); otherwise, S(X,m) is the union of S(Ni,m) for all namespaces Ni nominated by using-directives in X and its inline namespace set.

    3 Given X::m (where X is a user-declared namespace), or given ::m (where X is the global namespace), if S(X,m) is the empty set, the program is ill-formed. Otherwise, if S(X,m) has exactly one member, or if the context of the reference is a using-declaration (7.3.3), S(X,m) is the required set of declarations of m. Otherwise if the use of m is not one that allows a unique declaration to be chosen from S(X,m), the program is ill-formed.

    ::foo is a qualified-id with nested-name-specifier ::, so the name foo is looked up in global scope.

    S0(::,foo) contains the single declaration for namespace foo since there are no other declarations of foo in the namespace, and it has no inline namespaces. Since S0(::,foo) is not empty, S(::,foo) is S0(::,foo). (Note that since S0(::,foo) is not empty, namespaces nominated by using directives are never examined.)

    Since S(::,foo) has exactly one element, that declaration is used for ::foo.

    The name ::foo::baz is therefore a qualified-id with nested-name-specifier ::foo that nominates a namespace. There is again a single declaration of baz in namespace ::foo, so the name ::foo::baz refers to the class baz declaration.

    The behavior you observe in GCC 6+ is in fact a bug, filed as GCC PR 71173.

    EDIT: Lookup of foo::baz when it appears at global scope, a la:

    foo::baz bang;
    

    first requires a lookup of foo as an unqualified name. [basic.lookup.qual]/1 says this lookup only sees "namespaces, types, and templates whose specializations are types." [basic.scope.namespace]/1 tells us about namespace members and their scopes:

    The declarative region of a namespace-definition is its namespace-body. Entities declared in a namespace-body are said to be members of the namespace, and names introduced by these declarations into the declarative region of the namespace are said to be member names of the namespace. A namespace member name has namespace scope. Its potential scope includes its namespace from the name’s point of declaration onwards; and for each using-directive that nominates the member’s namespace, the member’s potential scope includes that portion of the potential scope of the using-directive that follows the member’s point of declaration.

    [basic.lookup.unqual]/4 tells us that the declaration of namespace foo is visible:

    A name used in global scope, outside of any function, class or user-declared namespace, shall be declared before its use in global scope.

    and para 2 says that class foo is also visible here:

    The declarations from the namespace nominated by a using-directive become visible in a namespace enclosing the using-directive; see 7.3.4. For the purpose of the unqualified name lookup rules described in 3.4.1, the declarations from the namespace nominated by the using-directive are considered members of that enclosing namespace.

    Since two declarations of different foo entities from differing namespaces are found, [namespace.udir]/6 tells us the use of foo is ill-formed:

    If name lookup finds a declaration for a name in two different namespaces, and the declarations do not declare the same entity and do not declare functions, the use of the name is ill-formed.

    Our name lookup for foo::baz dies before ever getting to baz.