[英]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::foo
, N1::foo
被ADL找到,它們都被添加到重載集,然后執行重載解析並且調用是不明確的。
順便說一句:不using N2::foo;
在main()
中, ::foo
將通過正常名稱查找找到,而N1::foo
也將通過 ADL 找到; 結果調用仍然不明確。
更新:
所以,這里的問題是為什么
::foo
不能被main
function中的“foo(N1::S{});
”調用?
因為using N2::foo;
, 名稱N2::foo
在main
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.