6

我有兩個用於演示目的的ADL代碼片段。這兩個片段已經由VC10,gcc & comeau C++編譯器編譯,結果對於所有三個都是相同的。爲什麼ADL優先於'std命名空間'中的函數,但等於用戶定義的命名空間中的函數?

< 1>針對使用用戶定義的命名空間的指令ADL:

#include <algorithm> 
namespace N 
{ 
    struct T {}; 
    void swap(T,T) {} 
} 

namespace M 
{ 
    void swap(N::T,N::T) {} 
} 

int main() 
{ 
    using M::swap; 
    N::T o1,o2; 
    swap(o1,o2); 
} 

編譯結果:

error C2668: 'M::swap' : ambiguous call to overloaded function 
could be 'void M::swap(N::T,N::T)' 
or  'void N::swap(N::T,N::T)' [found using argument-dependent lookup] 

這是預期爲ADL不優先於正常查找結果加上ADL不是二等公民,ADL搜索結果與正常(非ADL)未滿足的查找結合。這就是爲什麼我們有歧義。

< 2> ADL針對使用std命名空間的指令:

#include <algorithm> 
namespace N 
{ 
    struct T {}; 
    void swap(T,T) {} //point 1 
} 

namespace M 
{ 
    void swap(N::T,N::T) {} 
} 

int main() 
{ 
    using std::swap; 
    N::T o1,o2; 
    swap(o1,o2); 
} 

這一個編譯確定。

結果是編譯器選擇ADL結果(它採用std :: swap的先例),意思是N::swap()在'點1'將被調用。只有在缺乏'第一點'的情況下(比如說我註釋掉那一行),編譯纔會使用倒退std::swap來代替。

注意這種方式已被用在許多地方作爲覆蓋std::swap的方式。 但我的問題是,爲什麼ADL優先於'std命名空間'(case2),但被認爲等於用戶定義的命名空間函數(case1)?

C++標準中有這樣一段話嗎?

============================================== =================================== 閱讀有用的答案後編輯,可能對其他人有幫助。

所以我已經調整了我的代碼片段1 &現在歧義消失了,並且在重載解決方案時,編譯器更喜歡Nontemplate函數!

#include <algorithm> 
namespace N 
{ 
    struct T {}; 
    void swap(T,T) {} 
} 

namespace M 
{ 
    template<class T> 
    void swap(N::T,N::T) {} 
} 

int main() 
{ 
    using M::swap; 
    N::T o1,o2; 
    swap(o1,o2); //here compiler choose N::swap() 
} 

我也調整了我的片段2.只是爲了讓樂趣出現歧義!

#include <algorithm> 
namespace N 
{ 
    struct T {}; 

    template<class _Ty> inline 
    void swap(_Ty& _Left, _Ty& _Right) 
    { 
     _Ty _Tmp = _Move(_Left); 
     _Left = _Move(_Right); 
     _Right = _Move(_Tmp); 
    } 
} 

namespace M 
{ 
    void swap(N::T,N::T) {} 
} 

int main() 
{ 
    using std::swap; 
    N::T o1,o2; 
    swap(o1,o2); 
} 

gcc和科莫都表示,不確定性預期:

"std::swap" matches the argument list, the choices that match are: 
      function template "void N::swap(_Ty &, _Ty &)" 
      function template "void std::swap(_Tp &, _Tp &)" 

BTW VC10愚蠢像往常一樣讓這一個傳球ok了,除非我刪除了 '使用std ::交換'。

只是有點越寫:C++超載可能會非常棘手(30+頁C++標準),但在appendlix B的存在是一個非常可讀的10頁還有......

感謝所有的美好投入,現在很明顯。

回答

9

您的測試不會檢查是否ADL優先與否超過平常的查找,但重載決議,而如何確定最佳匹配。第二個測試用例工作的原因是std::swap是一個模板,並且在對完美匹配(由ADL發現)和模板執行重載解析時,非模板函數優先。

+0

值得注意的一點是,ADL屬於*名稱查找*,並且名稱查找沒有「優先級」的概念。 –

+0

@KerrekSB:我認爲David在名稱查找階段討論的是重載分辨率,而這一切都是關於選擇最佳匹配的。 – Gob00st

12

函數調用發生在幾個階段

  1. 名稱查找 - >把候選函數在一個所謂的超載設置
    • 這就是ADL發生,如果你的一部分有一個不合格的名稱查詢
  2. 模板參數扣除 - >對於過載集合中的每個模板
  3. 重載 - >挑選出最好的比賽

你混淆部分1部分3.名稱查找,實際上把兩隻swap函數重載集合({N::swap, std::swap}),但第3部分將決定,這一個最後打電話。

現在,由於std::swap是一個模板,標準說,非模板函數更加專業化比模板函數做重載決議的時候,你的<2>電話N::swap

§13.3.3 [over.match.best] p1

根據這些定義,一個可行的功能F1被定義爲比另一種可行的功能更好的功能F2如果[...]

  • F1是一個非模板函數和F2是一個函數模板的特[...]

†我建議this excellent series前三視頻拍攝對象。

+0

那麼,請評論downvote嗎? – Xeo

+0

感謝您的參考! – Gob00st

+0

@Xeo:dunno(關於downvote),第一句讓我困惑了一下,因爲編譯器沒有找到可能被調用的* all *函數。名稱查找全是關於找到一個子集。 –

相關問題