6

デモ用に ADL の 2 つのスニペットがあります。どちらのスニペットも VC10、gcc、comeau C++ コンパイラでコンパイルされており、結果は 3 つすべてで同じです。

<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 が第 2 級市民ではないため、ADL 検索結果が通常の (非 ADL) 条件のないルックアップと結合されるためです。だからこそ、私たちはあいまいさを持っています。

<2> std 名前空間のディレクティブを使用することに対する ADL:

#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」で呼び出されます。「ポイント 1」がない場合 (たとえば、その行をコメントアウトした場合) のみ、コンパイルはstd::swap代わりにフォールバックを使用します。

この方法は、 を上書きする方法として多くの場所で使用されていることに注意してくださいstd::swap。しかし、私の質問は、ADL が「std 名前空間」(case2) よりも優先されるのに、ユーザー定義の名前空間関数 (case1) と同等と見なされるのはなぜですか?

そう言っているC++標準の段落はありますか?

================================================== =============================== 有用な回答を読んだ後に編集すると、他の人に役立つかもしれません。

だから私はスニペット1を微調整しました&今ではあいまいさがなくなり、オーバーロードの解決を行うときに非テンプレート関数を明らかに優先してコンパイルします!

#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 と comeau はどちらも、予想どおりあいまいさを示しています。

"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 &)"

ところで、いつものように愚かな VC10 は、「using std::swap」を削除しない限り、これを問題なく通過させます。

もう少し書く必要があります: C++ のオーバーロードはトリッキーになる可能性があります (C++ 標準では 30+ ページ) が、付録 B には非常に読みやすい 10 ページがあります...

すべての素晴らしい入力に感謝します。これで明確になりました。

4

2 に答える 2

11

関数呼び出しはいくつかの段階で発生します†</sup>:

  1. 名前検索 -> 候補関数をいわゆるオーバーロード セットに入れます
    • これは、修飾されていない名前のルックアップがある場合に ADL が発生する部分です。
  2. テンプレート引数推定 -> オーバーロード セット内のすべてのテンプレート
  3. オーバーロード解決 -> 最適な一致を選択

パート 1 とパート 3 を混同しています。名前検索では、実際には両方のswap関数をオーバーロード セット ( {N::swap, std::swap}) に入れますが、最終的にどちらを呼び出すかはパート 3 で決定されます。

さて、std::swapはテンプレートであるため、標準では、オーバーロードの解決を行う場合、テンプレート以外の関数はテンプレート関数よりも特殊<2>化されていると述べていますN::swap

§13.3.3 [over.match.best] p1

これらの定義を考えると、実行可能な機能F1F2、[...]

  • F1非テンプレート関数でF2あり、関数テンプレートの特殊化です [...]

†この優れたシリーズの最初の 3 つのビデオをお勧めします。

于 2012-10-05T14:20:07.710 に答える
10

テストでは、ADL が通常のルックアップよりも優先されるかどうかはチェックされませんが、オーバーロードの解決によって最適な一致がどのように決定されるかがチェックされます。2 番目のテスト ケースが機能する理由std::swapはテンプレートであり、完全一致 (ADL によって検出) とテンプレートでオーバーロードの解決を実行すると、テンプレート化されていない関数が優先されます。

于 2012-10-05T14:23:59.933 に答える