All students are surprised by the behavior of C++ using-directives. Consider this snippet (Godbolt):
namespace NA {
int foo(Zoo::Lion);
}
namespace NB {
int foo(Zoo::Lion);
namespace NC {
namespace N1 {
int foo(Zoo::Cat);
}
namespace N2 {
int test() {
using namespace N1;
using namespace NA;
return foo(Zoo::Lion());
}
}
}
}
You might think test
would call NA
's foo(Zoo::Lion)
; but in fact it ends up calling N1
's foo(Zoo::Cat)
.
The reason is that using namespace NA
doesn't actually bring the names from NA
into the current scope; it brings them into the least common ancestor scope of NA
and N2
, which is ::
. And using namespace N1
doesn't bring the names from N1
into the current scope; it brings them into the least common ancestor scope of N1
and N2
, which is NC
. Here, I've drawn a pretty diagram:
Then normal unqualified lookup "looks up" the tree, in the order test
–N2
–NC
–NB
–::
, stopping as soon as it finds the name foo
in NC
. The foos higher up the tree, in NB
and ::
, aren't found by lookup because they've been hidden by the foo
in NC
.
I think I understand this mechanism well enough to explain how it works. What I don't understand is why. When C++ namespaces were being designed, why did they choose this utterly weird mechanism, instead of something more straightforward like "The names are brought into the current scope just like with a using-declaration" or "The names are brought into the global scope" or "The names are left where they are, separate lookups are done in each 'used' namespace and then merged together"?
What considerations went into the choice of this particular mechanism for C++?
I'm looking specifically for references to the design of C++ — books, blog posts, WG21 papers, D&E, ARM, reflector discussion, anything of that nature. I am specifically not looking for "opinion-based" answers; I'm looking for the real rationale specified at the time the feature was designed.
(I've read The Design and Evolution of C++ (1994), section 17.4 "Namespaces," but it doesn't even mention this wacky mechanism. D&E says: "A using-directive doesn't introduce names into the local scope; it simply makes the names from the namespace accessible." Which is true, but rather lacking in rationale; I'm hoping Stack Overflow can do better.)