Search code examples
c++namespacesambiguous

Global unnamed namespace ambiguity vs nested unnamed namespace ambiguity


Consider the following two code snippets:

Snippet A

#include <iostream>

namespace
{
    bool foo = false;
}

bool foo = true;

int main()
{
    std::cout << foo << std::endl;
}

Snippet B

#include <iostream>

namespace A
{
    namespace
    {
        bool foo = false;
    }

    bool foo = true;
}

int main()
{
    std::cout << A::foo << std::endl;
}

In Snippet A, foo's usage inside int main() is ambiguous, whilst in Snippet B it is not. Why is this the case?


Related: Anonymous Namespace Ambiguity


Solution

  • The behavior of unnamed namespaces is specified in §7.3.1.1 [namespace.unnamed]/p1:

    An unnamed-namespace-definition behaves as if it were replaced by

    inline_opt namespace unique { /* empty body */ }
    using namespace unique ;
    namespace unique { namespace-body }
    

    where inline appears if and only if it appears in the unnamed-namespace-definition, all occurrences of unique in a translation unit are replaced by the same identifier, and this identifier differs from all other identifiers in the entire program.

    In particular, note that the declarations inside the unnamed namespace is made visible in the surrounding scope via a using-directive using namespace unique;.

    In Snippet A, foo is unqualified, so the compiler performs unqualified name lookup (§3.4.1 [basic.lookup.unqual]). Relevant here is paragraph 2 of the subclause:

    2 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.

    Hence, unqualified name lookup finds both declarations of foo, and the name is ambiguous.

    In Snippet B, A::foo is qualified, so qualified name lookup rules apply. Since A is a namespace, the applicable subclause is §3.4.3.2 [namespace.qual]. As relevant here, the rule is specified in paragraph 2 of that subclause:

    For a namespace X and name m, the namespace-qualified lookup set S(X,m) is defined as follows: Let S'(X,m) be the set of all declarations of m in X and the inline namespace set of X (7.3.1). If S'(X,m) is not empty, S(X,m) is S'(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.

    In other words, qualified name lookup considers namespaces nominated by using-directives only if the name is not found in the specified namespace and its inline namespace set. Here, the name foo is found in namespace A, so the unnamed namespace nominated by the using-directive is not considered, and there's no ambiguity.

    If you write ::foo instead of foo in Snippet A, then qualified lookup rules would apply instead, and there would once again be no ambiguity.