2015-12-10 40 views
1

我有以下代碼:C++輸出流不與模板和命名空間的

#include <fstream> 

// Removing this namespace (keeping the content) makes it work 
namespace baz { 

class Bar { 
}; 

} 

std::ostream & operator<<(std::ostream & stream, baz::Bar & value) { 
    return stream; 
} 

// Removing this namespace (keeping the content) makes it work 
namespace goo { 

template <class Type> 
struct Point { 
}; 

// Removing this function makes it work 
template <class Type> 
std::ostream& operator<< (std::ostream& stream, const Point<Type> &point); 

void foo() { 
    baz::Bar test; 
    std::ofstream stream; 
    stream << test; 
} 

} 

它不彙編關於MSVC和失敗,出現以下錯誤消息:

錯誤C2679:二進制「 < <':沒有操作員發現它接受一個右手 的操作數類型的‘巴茲::酒吧’(或沒有可接受的轉化率)

但是,如果我刪除了兩個命名空間中的任何一個(保留所有內容)或刪除Point類的模板化<<函數,則一切正常。

這是MSVC中的錯誤嗎?如何在不刪除名稱空間或函數的情況下進行編譯?

+0

嘗試使用:: operator <<;'或將其移動到'namespace baz'。 –

回答

3

這是您代碼中的錯誤。爲功能設置的過載建立在多個階段:

  1. 從當前範圍向外找到匹配的函數名稱。在名稱空間中找到名稱時,將在此名稱空間中定義的所有重載添加到重載設置並停止此過程。
  2. 將通過參數相關查找(ADL)找到的所有重載添加到重載集。
  3. 確定過載集合是否包含唯一匹配和最佳過載。如果是這樣,那麼使用否則失敗。

你的問題是,你operator<<(std::ostream&, baz::Bar&)全局命名空間,而不是在Bar定義(在這種情況下baz)命名空間中定義。將過載放入命名空間baz中無論如何都需要使用名稱取決於模板參數的模板中的運算符:在這種情況下,第一個階段被省略,只有通過依賴於參數的查找找到的名稱才被檢測到。

順便說一句,在修復輸出操作符的位置時,您可能需要考慮將參數傳遞爲baz::Bar const&,因爲輸出操作符通常不會修改格式化的實體。

+0

你從目前的範圍向外說,當它發現第一個函數時,常規查找停止了嗎?爲什麼它找不到全球? – 0x499602D2

+0

0x499602D2:從當前上下文開始的查找在找到具有相應名稱的第一個函數時停止。在這個例子中,當找到'goo :: operator <<'並且全局名稱空間從不被搜索(當移除'goo :: operator <<'全局名字空間_is_被搜索時)時,搜索'operator <<'停止。由於參數還包含命名空間'std'(來自第一個參數)和命名空間'baz'(來自第二個參數),其中任何一個命名空間中的名稱都是從第二個部分(ADL)添加的。儘管如此添加的'operator <<'函數沒有一個匹配'baz :: Bar'。 –