Using directive vs using declaration

2020-02-26 01:14发布

问题:

Can you explain why this code works and prints 16;

#include <iostream>

namespace X {
int  p = 5;
}
namespace Y {
int  p = 16;

using namespace X;
}

int main()
{
std::cout << Y::p;
}

and why this code throws error of multiple declaration

#include <iostream>

namespace X {
int  p = 5;
}
namespace Y {
int  p = 16;

using X::p;
}

int main()
{
std::cout << Y::p;
}

I have heard that using directive isn't simply the process of using declaration of any name, as it seems to work differently

But I can't understand why, could you give some detailed explanation ??

similarly this one works fine printing 16, if I replace using the directive with just the declaration of X::p it will throw the same error

 #include <iostream>

namespace X {
    int  p = 5;
}

int  p = 16;

using namespace X;

int main()
{
    std::cout << ::p;
    std::cout << "\n";

    return 0;
}

回答1:

The key difference is that a using declaration is, well, a declaration. While a using directive is not. Sounds stupid, I know, but that's the nature of the difference. The former actually adds declarations to a declarative region, while the latter only makes certain names usable in a declarative region.

Making a name usable in a certain scope, as opposed to declaring it, is intended to be a much weaker thing. The former is not considered during qualified lookup, if there's another declaration with the same name, as [basic.scope.hiding]/4 says:

During the lookup of a name qualified by a namespace name, declarations that would otherwise be made visible by a using-directive can be hidden by declarations with the same name in the namespace containing the using-directive

And that pretty much explains your first code snippet. There's a declaration for that name, on account of the using declaration, so the name made visible by the using directive isn't considered. Doesn't matter which comes first, a declaration is always stronger.

Your second code snippet has two declarations for p in Y. And when it comes to those, the usual rules for declarations apply. The second one must declare the same thing, or the program is ill-formed. Nothing more to it, really.

Finally, in your third code snippet, it's more of the same as in your first code snippet. [basic.lookup.qual]/4 says this:

A name prefixed by the unary scope operator ​::​ ([expr.prim]) is looked up in global scope, in the translation unit where it is used. The name shall be declared in global namespace scope or shall be a name whose declaration is visible in global scope because of a using-directive ([namespace.qual]). The use of ​::​ allows a global name to be referred to even if its identifier has been hidden.

So other than the namespace being looked up, everything else is just like your first example. You both declare it, and make it available by the using directive. The first paragraph I quoted determines which one must be picked.