简体   繁体   English

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

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

Actually, the below code can not be compiled with Clang using this command:实际上,下面的代码不能用 Clang 使用这个命令编译:

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

I just want to mimic the same behavior as "swapping idiom" in C++ to use "using-directive" to enable ADL.我只想模仿与 C++ 中的“swapping idiom”相同的行为,以使用“using-directive”来启用 ADL。 But where am I wrong with the following code?但是下面的代码哪里错了? The expected calling priority should be the: N1::foo > N2::foo > ::foo , right?预期的调用优先级应该是: 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{});
}

Error messages:错误信息:

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.

Updated:更新:

I changed N2::foo to a template method which can mimic std::swap to some extend.我将 N2::foo 更改为可以在某种程度上模仿 std::swap 的模板方法。 So, the question here is why ::foo can not be called by " foo(N1::S{}); " in the main function?所以,这里的问题是为什么::foo不能被main function中的“ foo(N1::S{}); ”调用? Since the function should be much properer than a template function to be called when they have the same priority.由于 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.
}

In this case, normal name lookup finds N2::foo , and N1::foo is found by ADL , they're both added to the overload set, then overload resolution is performed and the calling is ambiguous.在这种情况下, 正常的名称查找找到N2::fooN1::fooADL找到,它们都被添加到重载集,然后执行重载解析并且调用是不明确的。

BTW: Without using N2::foo;顺便说一句:不using N2::foo; in main() , ::foo will be found by normal name lookup, and N1::foo is found by ADL too;main()中, ::foo将通过正常名称查找找到,而N1::foo也将通过 ADL 找到; as the result the calling is still ambiguous.结果调用仍然不明确。

Updated:更新:

So, the question here is why ::foo can not be called by " foo(N1::S{}); " in the main function?所以,这里的问题是为什么::foo不能被main function中的“ foo(N1::S{}); ”调用?

Because with the usage of using N2::foo;因为using N2::foo; , the name N2::foo is introduced in the main function. When calling foo the name N2::foo will be found at the scope of main , then name lookup stops, the further scope (the global namespace) won't be examined, so ::foo won't be found and added to overload set at all. , 名称N2::foomain function 中引入。当调用foo时,名称N2::foo将在main的 scope 中找到,然后名称查找停止,进一步的 scope(全局命名空间)将不会被检查,因此根本不会找到::foo并将其添加到重载集中。 As the result N2::foo is called for both cases.结果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.名称查找按如下所述检查范围,直到它找到至少一个任何类型的声明,此时查找停止并且不再检查其他范围。

BTW: If you put using N2::foo;顺便说一句:如果你using N2::foo; in global namespace before main , foo(N1::S{});main之前的全局命名空间中, foo(N1::S{}); would call ::foo .会打电话给::foo Both N2::foo and ::foo are found by name lookup and ::foo wins in overload resolution. N2::foo::foo都是通过名称查找找到的,并且::foo在重载解析中获胜。

LIVE居住

First you have the ordinary lookup that searches from inner scopes to outer scopes and stops at first match hiding overload from later scopes.首先,你有普通的查找,从内部范围搜索到外部范围,并在第一次匹配时停止,从后面的范围隐藏重载。 Then, when ADL is triggered, it will add additional associated entities and namespaces to the search.然后,当触发 ADL 时,它将向搜索中添加额外的关联实体和名称空间。

Thus, in your case, ADL lookup does not add anything to the overload set.因此,在您的情况下,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
}

Now, if I uncomment your S1 version, it wins: This is what you get:现在,如果我取消注释你的 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
}

You get the same thing with swap.你用交换得到同样的东西。 In this case, Foo::swap is used because it is in N1 namespace:在这种情况下,使用 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);
}

But, if you move the Foo specific swap in the global ns, then std::swap get called:但是,如果您在全局 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