簡體   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