繁体   English   中英

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

[英]How does ADL affect this piece of C++ code?

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

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

我只想模仿与 C++ 中的“swapping idiom”相同的行为,以使用“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 function中的“ foo(N1::S{}); ”调用? 由于 function 应该比模板 function 在具有相同优先级时调用更合适。

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

在这种情况下, 正常的名称查找找到N2::fooN1::fooADL找到,它们都被添加到重载集,然后执行重载解析并且调用是不明确的。

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

更新:

所以,这里的问题是为什么::foo不能被main function中的“ foo(N1::S{}); ”调用?

因为using N2::foo; , 名称N2::foomain function 中引入。当调用foo时,名称N2::foo将在main的 scope 中找到,然后名称查找停止,进一步的 scope(全局命名空间)将不会被检查,因此根本不会找到::foo并将其添加到重载集中。 结果N2::foo在这两种情况下都会被调用。

名称查找按如下所述检查范围,直到它找到至少一个任何类型的声明,此时查找停止并且不再检查其他范围。

顺便说一句:如果你using N2::foo; main之前的全局命名空间中, foo(N1::S{}); 会打电话给::foo N2::foo::foo都是通过名称查找找到的,并且::foo在重载解析中获胜。

居住

首先,你有普通的查找,从内部范围搜索到外部范围,并在第一次匹配时停止,从后面的范围隐藏重载。 然后,当触发 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);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM