Why can an (irrelevant) using declaration reconcile overload ambiguity with Argument-Dependent Lookup?

六眼飞鱼酱① 提交于 2019-12-10 19:07:23

问题


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.

  1. A using declaration, like using std::swap, is a declaration. It introduces a declaration of swap into the declarative region of the function.

  2. 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.

  3. 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:

  1. std::swap is declared inside swap(Foo, Foo).
  2. The names inside myNS are made available to swap(Foo, Foo), as though they were declared with it in the same namespace.
  3. The declaration added in #1, hides the one made visible in #2.
  4. ::swap can be found by ADL (despite also being hidden by #1), but myNS::swap can't. Since the myNS 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 swaps 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!