如何解决ADL如何影响这段C ++代码?
实际上,以下代码无法使用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
在函数foo(N1::S{});
中不能被main
调用?由于该函数比具有相同优先级的模板函数要合适得多。
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找到N2::foo
,ADL找到N1::foo
,它们都被添加到重载集中,然后执行重载解析并通话不明确。
BTW:在using N2::foo;
中没有main()
的情况下,将通过普通名称查找找到::foo
,并且也由ADL找到N1::foo
;结果,通话仍然不明确。
已更新:
所以,这里的问题是,为什么
::foo
函数中的foo(N1::S{});
无法被main
调用?
由于使用using N2::foo;
,因此N2::foo
函数中引入了名称main
。调用foo
时,将在N2::foo
的范围内找到名称main
,然后名称查找停止,不会检查其他范围(全局名称空间),因此{{1 }}根本找不到并添加到过载集。结果,两种情况都调用::foo
。
名称查找按如下所述检查scopes,直到找到至少一个任何类型的声明为止,这时查找停止,并且不检查其他范围。
顺便说一句:如果将N2::foo
放在using N2::foo;
之前的全局名称空间中,则main
将调用foo(N1::S{});
。 ::foo
和N2::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
}
通过swap获得相同的结果。在这种情况下,使用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,r.f); // because ADL does not add the global ns to the
// ns to be searched for
}
}
int main() {
N2::S s1,s2);
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。