7

私はこの問題に数回噛まれたので、同僚もそうです。コンパイルするとき

#include <deque>
#include <boost/algorithm/string/find.hpp>
#include <boost/operators.hpp>

template< class Rng, class T >    
typename boost::range_iterator<Rng>::type find( Rng& rng, T const& t ) {
      return std::find( boost::begin(rng), boost::end(rng), t );
}

struct STest {
      bool operator==(STest const& test) const { return true; }
};

struct STest2 : boost::equality_comparable<STest2>   {
      bool operator==(STest2 const& test) const { return true; }
};

void main() {
      std::deque<STest> deq;
      find( deq, STest() ); // works
      find( deq, STest2() ); // C2668: 'find' : ambiguous call to overloaded function
}

... 2番目の検索をコンパイルするときに、VS9コンパイラが失敗します。これはSTest2、ブースト名前空間で定義された型から継承し、コンパイラがを検出するADLを試行するようにトリガーするためboost::algorithm::find(RangeT& Input, const FinderT& Finder)です。

find(…)明らかな解決策は、呼び出しの前に「 」を付けること::ですが、なぜこれが必要なのですか?グローバル名前空間には完全に有効な一致があるのに、なぜ引数依存のルックアップを呼び出すのですか?誰かがここで理論的根拠を説明できますか?

4

4 に答える 4

7

ADLは、「通常の」過負荷解決が失敗したときに使用するフォールバックメカニズムではありません。ADLによって検出された関数は、通常のルックアップによって検出された関数と同じように実行可能です。

ADLがフォールバックソリューションである場合、より適切に一致するがADLを介してのみ表示される別の関数がある場合でも、関数が使用されていれば、簡単にトラップに陥る可能性があります。これは、(たとえば)演算子のオーバーロードの場合に特に奇妙に見えるでしょう。適切な名前空間operator==に完全に良いものが存在する場合に暗黙的に変換される可能性のあるタイプについて、2つのオブジェクトを比較することは望ましくありません。operator==

于 2010-09-09T10:58:38.113 に答える
3

私はこの問題について調査したばかりなので、明白な答えを自分で追加します。

C ++ 033.4.2

§2関数呼び出しの引数タイプTごとに、0個以上の関連付けられた名前空間のセットがあります[...]名前空間とクラスのセットは次の方法で決定されます。

[...]

— Tがクラスタイプ(ユニオンを含む)の場合、関連するクラスは次のとおりです。クラス自体。メンバーであるクラス(ある場合)。およびその直接および間接の基本クラス。関連する名前空間は、関連するクラスが定義されている名前空間です。

§2a名前の通常の非修飾ルックアップでクラスメンバー関数の宣言が見つかった場合、関連する名前空間とクラスは考慮されません。それ以外の場合、関数名のルックアップによって検出された宣言のセットは、通常の非修飾ルックアップを使用して検出された宣言のセットと、引数タイプに関連付けられた名前空間およびクラスで検出された宣言のセットの和集合です。

少なくともそれは標準的な適合ですが、私はまだここでの理論的根拠を理解していません。

于 2010-09-09T10:52:23.953 に答える
3

mystreamから継承するaを考えてみましょうstd::ostream。あなたのタイプは、std名前空間で通常<<定義されているすべての演算子をサポートすることを望みます。std::ostreamしたがって、基本クラスはADLに関連付けられたクラスです。

これも置換原則に基づいていると思います。クラスの名前空間内の関数は、そのインターフェイスの一部と見なされます(Herb Sutterの「クラスの内容」を参照)。したがって、基本クラスで機能するインターフェイスは、派生クラスでも機能し続ける必要があります。

ADLを無効にすることでこれを回避することもできます。

(find)( deq, STest2() );
于 2010-09-09T10:56:33.530 に答える
1

私はあなたが自分で問題を述べたと思います:

グローバル名前空間で

グローバル名前空間の関数は最後と見なされます。定義上、最も外側のスコープです。(呼び出しの観点から)より近いスコープで見つかった同じ名前(必ずしも適用可能ではない)の関数が最初に取得されます。

template <typename Rng, typename T>
typename Rng::iterator find( Rng& rng, T const& t );

namespace foo
{
  bool find(std::vector<int> const& v, int);

  void method()
  {
    std::deque<std::string> deque;
    auto it = find(deque, "bar");
  }
}

ここでは(許可されている、vectorまたはdequeincludeがない限りalgorithm)、名前の検索中に取得される唯一のメソッドは次のとおりです。

bool foo::find(std::vector<int> const&, int);

何らかの形で含まれている場合algorithmは、次のものもあります。

template <typename FwdIt>
FwdIt std::find(FwdIt begin, FwdIt end,
                typename std::iterator_traits<FwdIt>::value_type const& value);

そしてもちろん、過負荷の解決は失敗し、一致するものがないことを示します。

名前検索は非常に馬鹿げていることに注意してください。アリティも引数タイプも考慮されていません。

したがって、C++で使用する必要があるフリー関数は2種類だけです。

  • クラスのインターフェースの一部であり、同じ名前空間で宣言され、ADLによって取得されたもの
  • そうでないもの、およびこのタイプの問題を回避するために明示的に修飾する必要があるもの

これらのルールに違反した場合、含まれているものによっては機能する場合と機能しない場合があり、それは非常に厄介です。

于 2010-09-09T12:03:33.450 に答える