问题
This is a follow up of the question here on function overload with Argument-Dependent Lookup (ADL). I wanted to check my understanding of the rules under these circumstances so I wrote some test code.
First, there is no swap for HasPtr class in std, of course, so I wrote a namespace of my own which contains a HasPtr version of swap in addition to the one already defined in global scope. The using declaration works as what I expected -- an error of ambiguity was produced because there is a HasPtr version of swap already defined as does in "C++ Primer", 5ed.
Then I want to see what will happen if I change using declaration to using directive. The book says the compiler will keep silent until the function is actually called. I wanna verify that so the code is as follows:
#include <string>
class HasPtr {
public:
friend void swap(HasPtr&, HasPtr&);
std::string *ps;
};
void swap(HasPtr &lhs, HasPtr &rhs) {
swap(lhs.ps, rhs.ps); // swap the pointers, not the string data
}
namespace myNS {
void swap(HasPtr &lhs, HasPtr &rhs) {
std::string s = "in my name space";
swap(lhs.ps, rhs.ps); // swap the pointers, not the string data
}
}
class Foo {
friend void swap(Foo &lhs, Foo &rhs);
HasPtr h;
};
void swap(Foo &lhs, Foo &rhs) {
using std::swap; //<- commenting this line will cause error
using namespace myNS;
swap(lhs.h, rhs.h);
}
int main() {
Foo f1, f2;
swap(f1, f2);
}
Strange thing happened in line 27 (using std::swap;
). If I commented it out, the name myNS::swap which has exactly the same signature as the one already defined in global scope was lifted to global scope and consequently causes an error of overloading ambiguity, as I expected.
But, if I do not comment line 27 and compile, no error of ambiguity is reported. And the program executes the ::swap originally defined in global scope as if the using directive using namespace myNS;
does not lift myNS::swap so that it is not added to the candidate set for overloading. I just couldn't understand this phenomenon. Why can a using declaration from an irrelevant namespace (std certainly does not contain a HasPtr version of swap) reconcile overload ambiguity under ADL? Why is it the original ::swap, instead of its rival in myNS, that is selected to execute? Does the line 27 have any side effects to overloading process (say, suppressing names from lifted namespace so original names have a higher priority)? Thank you for your answers.
The problem can be reproduced in Visual Studio 2015 Update 3 on Windows 7 and in GCC 4.8.4 on ubuntu 14.04, both 64-bit.
回答1:
The mechanics at play here are three fold.
A using declaration, like
using std::swap
, is a declaration. It introduces a declaration ofswap
into the declarative region of the function.A using directive, on the other hand, does not introduce declarations into the current declarative region. It only allows unqualified lookup to treat names from the nominated namespaces, as though they were declared in the nearest enclosing namespace of the current declarative region.
Declarations in smaller declarative regions hide declarations from the larger enclosing declarative regions.
With respect to the above, the way you have it setup is like this:
std::swap
is declared insideswap(Foo, Foo)
.- The names inside
myNS
are made available toswap(Foo, Foo)
, as though they were declared with it in the same namespace. - The declaration added in #1, hides the one made visible in #2.
::swap
can be found by ADL (despite also being hidden by #1), butmyNS::swap
can't. Since themyNS
version is both hidden, and not found by ADL, it doesn't conflict with anything.
When you remove the declaration of std::swap
, now you have myNS::swap
visible. ADL finds ::swap
as well, giving you two overloads. They are both valid overloads, and produce an obvious ambiguity.
回答2:
Note that using-directive and using-declaration have different effect:
(emphasis mine)
using-directive: From the point of view of unqualified name lookup of any name after a using-directive and until the end of the scope in which it appears, every name from ns_name is visible as if it were declared in the nearest enclosing namespace which contains both the using-directive and ns_name.
That means for using namespace myNS;
, the name myNS::swap
is visible as if it were declared in the global scope. If using std::swap;
is commented, then 2 swap
s will be found at the global scope and then cause the ambiguity.
If using std::swap;
is uncommented, the swap
from std
namespace will be found at the function scope, then the name lookup stops, the further global scope won't be checked. Note that the global ::swap
could be found by ADL, with the addition of std::swap
they will be considered in overload resolution, and ::swap
is selected then no ambiguity. myNS::swap
won't kick in for this case.
name lookup examines the scopes as described below, until it finds at least one declaration of any kind, at which time the lookup stops and no further scopes are examined.
来源:https://stackoverflow.com/questions/46785093/why-can-an-irrelevant-using-declaration-reconcile-overload-ambiguity-with-argu