簡體   English   中英

由ADL引起的對模板化函數的模糊調用

[英]Ambiguous call to templated function due to ADL

我幾次被這個問題所困擾,所以我的同事也是如此。 編譯時

#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
}

...編譯第二個查找時,VS9編譯器失敗。 這是因為STest2繼承了boost命名空間中定義的類型,觸發編譯器嘗試ADL,它找到boost::algorithm::find(RangeT& Input, const FinderT& Finder)

一個明顯的解決方案是使用“ :: ”為find(…)添加前綴,但為什么這是必要的? 全局命名空間中存在完全有效的匹配,那么為什么要調用Argument-Dependent Lookup? 任何人都可以解釋這里的理由嗎?

ADL不是在“正常”重載決策失敗時使用的回退機制,ADL找到的函數與正常查找找到的函數一樣可行。

如果ADL是一個后備解決方案,那么即使有另一個功能更好匹配但只能通過ADL可見,你可能很容易陷入陷阱。 在(例如)運算符重載的情況下,這似乎特別奇怪。 您不希望通過operator==對兩個對象進行比較,以便在適當的命名空間中存在完美的operator==時可以隱式轉換為它們。

我會自己添加明顯的答案,因為我只是對這個問題進行了一些研究:

C ++ 03 3.4.2

§2對於函數調用中的每個參數類型T,都有一組零個或多個關聯的命名空間[...]命名空間和類的集合按以下方式確定:

[...]

- 如果T是類類型(包括聯合),則其關聯的類是:類本身; 它所屬的成員,如果有的話; 及其直接和間接基類 其關聯的命名空間是定義其關聯類的命名空間。

§2a 如果名稱的普通非限定查找找到類成員函數的聲明,則不考慮關聯的名稱空間和類。 否則,通過查找函數名稱找到的聲明集是使用普通非限定查找找到的聲明集的並集,以及在與參數類型關聯的名稱空間和類中找到的聲明集。

至少它符合標准,但我仍然不理解這里的基本原理。

考慮一個繼承自std::ostreammystream 您希望您的類型支持通常在std命名空間中為std::ostream定義的所有<<運算符。 因此基類是ADL的關聯類。

我認為這也取決於替換原則 - 類'命名空間中的函數被認為是其接口的一部分(參見Herb Sutter的“類中有什么?”)。 因此,在基類上工作的接口應該仍然在派生類上工作。

您還可以通過禁用ADL來解決此問題:

(find)( deq, STest2() );

我想你自己說過這個問題:

在全局命名空間中

全局命名空間中的函數被認為是最后的。 根據定義,它是最外層的范圍。 將首先拾取在更近的范圍(從調用的角度)中找到的具有相同名稱(不一定適用)的任何功能。

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");
  }
}

這里(除非vectordeque包含允許的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);

當然,重載解析將失敗,說明沒有匹配。

請注意,名稱查找非常愚蠢:既不考慮arity也不考慮參數類型!

因此,在C ++中只應使用兩種自由函數:

  • 那些屬於類的接口的一部分,在同一名稱空間中聲明,由ADL拾取
  • 那些不是,你應該明確有資格避免這種類型的問題

如果你不遵守這些規則,它可能會起作用,也可能不起作用,這取決於所包含的內容,這非常尷尬。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM