Search code examples
c++scopenamespacesusing

Ambiguous variable reference in C++


I have this piece of code

#include <iostream>

namespace ns{
    int a = 10;
}

using namespace ns;
int a = 20;

int main(){
    std::cout << a;
}

As per my understanding, when std::cout << a is evaluated, a is searched for in main's local scope. If not found, the lookup is performed in the global scope, where a is found. So, I was simply expecting it to print 20, but the code doesn't compile, claiming that

error: reference to 'a' is ambiguous

However, this code works :

#include <iostream>

namespace ns{
    int a = 10;
}

using namespace ns;
int a = 20;

int main(){
    std::cout << ::a;
}

It prints 20. Why does this difference arise? Shouldn't the two codes be equivalent, considering that a is not defined in main's scope, and the lookup is performed in the global scope in both the cases?

This is related to a similar question, but I want to specifically understand the difference caused by replacing a with ::a and how the two are not equivalent, rather than just ambiguous variable references.


Solution

  • Take a look at https://en.cppreference.com/w/cpp/language/namespace, particularly the

    Using-directive does not add any names to the declarative region in which it appears (unlike the using-declaration), and thus does not prevent identical names from being declared.

    Since you're "only" doing a using ns instead of a using ns::a, you're not introducing the a into the global namespace. You are just telling the compiler to search the ns namespace in addition to the global one.

    Thus, when you declare the a variables, there is no problem because one is in ns and one is in the global namespace. However, when you lookup an unqualified a, then all the ns members are considered to be part of the global namespace as well (because of the using), so now there are two a in the same namespace.

    In your second example, the ::a explicitly tells to use the global namespace; it's not an unqualified lookup anymore. For this, the https://en.cppreference.com/w/cpp/language/qualified_lookup has this to say:

    Qualified lookup within the scope of a namespace N first considers all declarations that are located in N ... If there are no declarations in that set then it considers declarations in all namespaces named by using-directives found in N ...

    In other words, the "using" namespaces are considered AFTER the actual namespace, so there's no ambiguity anymore.