Reputation: 467
How do names get resolved in C++ namespace?
I'm especially interested in cases where a name is provided in multiple different ways, for instance by a parent namespace and a using namespace
.
Take this piece of code:
namespace ns1 {
static auto str = "ns1::str";
}
namespace ns2 {
static auto str = "ns2::str";
namespace sub {
using namespace ns1;
auto f() { return str; }
}
}
With my compiler, ns2::sub::f()
returns "ns2::str"
. I expected it to return "ns1::str"
, because using namespace ns1
appears in ns2::sub
.
And now this piece:
static auto str = "str";
namespace ns1 {
static auto str = "ns1::str";
}
namespace ns2 {
using namespace ns1;
auto f() { return str; }
}
I expected it to behave like the previous case. Instead it doesn't compile:
error: reference to ‘str’ is ambiguous
What's the logic behind this?
Upvotes: 3
Views: 393
Reputation: 170279
The rules to keep in mind for using-directives are the following
[namespace.udir]
2 A using-directive specifies that the names in the nominated namespace can be used in the scope in which the using-directive appears after the using-directive. During unqualified name lookup ([basic.lookup.unqual]), the names appear as if they were declared in the nearest enclosing namespace which contains both the using-directive and the nominated namespace. [ Note: In this context, “contains” means “contains directly or indirectly”. — end note ]
3 A using-directive does not add any members to the declarative region in which it appears.
Paragraph 3 is what ensures that we don't get an error until name lookup is attempted. It doesn't add a declaration, and so long as str
isn't looked up by unqualified name lookup, we get no conflict. Whereas paragraph 2 tells us how the names are logically laid out. We can apply it to your two examples.
The nearest enclosing namespace that contains both the using namespace ns1;
and the namespace ns1
itself is the global namespace. So the name ns1::str
behaves as if we had written
static auto str = "ns1::str";
namespace ns2 {
static auto str = "ns2::str";
namespace sub {
auto f() { return str; }
}
}
So, name lookup would only find ns2::str
, because it would hide any str
from and enclosing namespace (which is the "as-if" situation in the code above).
Again, the nearest enclosing namespace is the global one. But this time, the layout we get is different
static auto str = "str"; // This is the one declared
static auto str = "ns1::str"; // This is the one "as-if" made available
namespace ns2 {
auto f() { return str; }
}
Obviously, the above "equivalent" code snippet isn't a valid one, the declarations would be conflicting ones. But it is an illustration to the source of the conflict. The name coming from that scope is in conflict. So unqualified name lookup fails.
In the absence of using-directives, unqualified name lookup behaves in a fairly intuitive manner. Scopes are looked up from inner to outer, until a declaration for a name is found.
Upvotes: 2