ADL如何影响这段C++代码?

How does ADL affect this piece of C++ code?

实际上,下面的代码不能用这个命令用 Clang 编译:

clang++ -std=c++11 test.cc -o test.

我只想模仿与 C++ 中的“交换习惯用法”相同的行为,以使用“using-directive”来启用 ADL。但是下面的代码哪里错了?预期的呼叫优先级应该是:N1::foo > N2::foo > ::foo,对吗?

namespace N1 {
  struct S {};
  void foo(S s) {
    std::cout << "called N1::foo.";
  }
}
namespace N2 {
  void foo(N1::S s) {
    std::cout << "called N2::foo.";
  }
}
void foo(N1::S s) {
  std::cout << "called foo.";
}
int main() {
  using N2::foo;  
  foo(N1::S{});
}

错误信息:

test.cc:54:3: error: call to 'foo' is ambiguous
  foo(N1::S{});
  ^~~
test.cc:40:8: note: candidate function
  void foo(S s) {
       ^
test.cc:45:8: note: candidate function
  void foo(N1::S s) {
       ^
1 error generated.

更新:

我将 N2::foo 更改为可以在某种程度上模仿 std::swap 的模板方法。那么,这里的问题是为什么::foo不能被main函数中的“foo(N1::S{});”调用呢?由于该函数应该比具有相同优先级的模板函数调用更合适。

namespace N1 {
  struct S {};
  /*
  void foo(S s) {
    std::cout << "called N1::foo, specific one." << '\n';
  }
  */
}
namespace N2 {  // as a fallback to unqualified name which has no user-defined overload.
  template<typename T>
  void foo(T) {
    std::cout << "called N2::foo, generic one." << '\n';
  }
}
void foo(N1::S s) {
  std::cout << "called foo." << '\n';
}
int main() {
  using N2::foo;
  foo(N1::S{});
  foo(10);  // use generic version.
}

在这种情况下,normal name lookup finds N2::foo, and N1::foo is found by ADL,它们都被添加到重载集中,然后执行重载解析并且调用不明确。

顺便说一句:如果 main() 中没有 using N2::foo;::foo 将通过正常的名称查找找到,而 N1::foo 也会通过 ADL 找到;结果调用仍然不明确。

更新:

So, the question here is why ::foo can not be called by "foo(N1::S{});" in the main function?

因为随着using N2::foo;的使用,在main函数中引入了名字N2::foo。当调用 foo 时,名称 N2::foo 将在 main 的范围内找到,然后名称查找停止,不会检查更远的范围(全局名称空间),因此 ::foo 根本不会被发现并添加到重载集。结果 N2::foo 在这两种情况下都被调用。

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.

顺便说一句:如果在 main 之前将 using N2::foo; 放入全局命名空间,foo(N1::S{}); 将调用 ::fooN2::foo::foo 都是通过名称查找找到的,::foo 在重载解析中获胜。

LIVE

首先,您有普通的查找,从内部范围搜索到外部范围,并在第一次匹配时停止,从后面的范围隐藏过载。然后,当 ADL 被触发时,它会添加额外的关联实体和命名空间到搜索中。

因此,在您的情况下,ADL 查找不会向重载集添加任何内容。

#include <iostream>

namespace N1 {
  struct S {};
  /*
  void foo(S s) {
    std::cout << "called N1::foo, specific one." << '\n';
  }
  */
}
namespace N2 { 
  template<typename T>
  void foo(T) {
    std::cout << "called N2::foo, generic one." << '\n';
  }
}
void foo(N1::S s) {
  std::cout << "called foo." << '\n';
}
int main() {
  using N2::foo;
  foo(N1::S{}); // ordinary lookup finds N2 in main scope and stops.
                // adl lookup add N1 ns to the additionnal ns set but finds nothing
                // overload set = N2::foo by ordinary lookup
}

现在,如果我取消注释你的 S1 版本,它就赢了!这就是你得到的:

#include <iostream>

namespace N1 {
  struct S {};
  void foo(S s) {
    std::cout << "called N1::foo, specific one." << '\n';
  }
}
namespace N2 { 
  template<typename T>
  void foo(T) {
    std::cout << "called N2::foo, generic one." << '\n';
  }
}
void foo(N1::S s) {
  std::cout << "called foo." << '\n';
}
int main() {
  using N2::foo;
  foo(N1::S{}); // ordinary lookup finds N2 in main scope and stops
                // adl lookup add N1 ns to the additionnal ns set and finds N1::foo
                // overload set = N2::foo by ordinary lookup, N1::foo by ADL, N1::foo wins
}

交换也一样。在这种情况下,使用 Foo::swap 因为它在 N1 命名空间中:

#include <iostream>

namespace N1 {
    struct Foo {

    };
    void swap(Foo& , Foo&) {
        std::cout << "swap Foo" << std::endl;
    }
}

namespace N2 {

struct S {
    N1::Foo f;
};

void swap(S& l,S& r) {
    using std::swap; // overload set is std::swap by ordinary lookup
    swap(l.f, r.f); // and Foo::swap by ADL, Foo::swap wins
}
}

int main() {
    N2::S s1,s2;
    swap(s1,s2);
}

但是,如果您在全局 ns 中移动特定于 Foo 的交换,那么 std::swap 会被调用:

#include <iostream>

namespace N1 {
    struct Foo {

    };
}

void swap(N1::Foo& , N1::Foo&) {
    std::cout << "swap Foo" << std::endl;
}

namespace N2 {

struct S {
    N1::Foo f;
};

void swap(S& l,S& r) {
    using std::swap; // overload set is std::swap by ordinary lookup
    swap(l.f, r.f); // because ADL does not add the global ns to the
                    // ns to be searched for
}
}

int main() {
    N2::S s1,s2;
    swap(s1,s2);
}