2011-03-04 69 views
30

我正在使用一個在全局名稱空間中定義輸出流運算符(運算符< <)的庫。在我自己的命名空間中,我總是在全局命名空間中聲明這樣的運算符,並且從來沒有遇到過問題。但現在由於各種原因,我需要在自己的命名空間中聲明這些運算符,並且突然間,編譯器似乎無法再找到在庫中聲明的運算符。命名空間和運算符分辨率

這裏有一個簡單的例子,說明我的問題:

#include <iostream> 

namespace A 
{ 
    struct MyClass {}; 
} 

std::ostream & operator<<(std::ostream & os, const A::MyClass &) 
    { os << "namespace A"; return os; } 

namespace B 
{ 
    struct MyClass {}; 

    std::ostream & operator<<(std::ostream & os, const B::MyClass &) 
     { os << "namespace B"; return os; } 
} 

namespace B 
{ 
    void Test() 
    { 
     std::cout << A::MyClass() << std::endl; 
     std::cout << B::MyClass() << std::endl; 
    } 
} 

int main() 
{ 
    B::Test(); 
    return 1; 
} 

我收到以下錯誤:

error: no match for ‘operator<<’ in ‘std::cout << A::MyClass()’ 

需要注意的是,如果這兩個運營商的命名空間內,或者,如果他們都在全局名稱空間中,代碼編譯並正確執行。

我真的很想知道發生了什麼,以及用命名空間定義這些運算符的「良好實踐」。

謝謝!

+0

+1很奇怪!如果我把'Test'放在'namespace A'中,它會編譯並正確執行! – 2011-03-04 15:26:37

回答

0

這是因爲你的第一個operator<<()被命名空間A.

+2

「請注意,如果兩個運算符都位於命名空間內,或者它們都位於全局命名空間中,則代碼將正確編譯並執行。」他正在尋找爲什麼它不起作用的理由;他已經知道如何解決它。 – 2011-03-04 15:25:20

31

由於Test是命名空間B內編譯看到運營商在該命名空間,並指出它不具有匹配的簽名以外定義。它還嘗試在名稱空間A中找到包含該類的運算符,但無法在其中找到該運算符。因爲名稱空間B中已經有這樣一個運算符(具有錯誤的簽名),它不會去嘗試在全局範圍內找到一個運算符。

它不搜索全局的原因大致如下。我首先引用標準,然後嘗試解釋它。

從3.4/1:

...Name lookup may associate more than one declaration with a name if it finds the name to be a function name; the declarations are said to form a set of overloaded functions (13.1). Overload resolution (13.3) takes place after name lookup has succeeded.

當我讀到此,當編譯器試圖找到一個函數(你的運營商在這方面),它首先試圖做名稱查找發現功能第一。然後接下來嘗試從一組重載中選擇正確的功能。

現在從3.4.1/6:

A name used in the definition of a function(26) that is a member of namespace N (where, only for the purpose of exposition, N could represent the global scope) shall be declared before its use in the block in which it is used or in one of its enclosing blocks (6.3) or, shall be declared before its use in namespace N or, if N is a nested namespace, shall be declared before its use in one of N’s enclosing namespaces.

讓我們打破下來。您在命名空間級別函數中使用operator<<,所以本節適用。它將嘗試使用上述優先級來找到該運算符。您的運算符未在當前塊中聲明或封閉塊(這指的是在函數中嵌套{})。然而,下一部分匹配「...應在名稱空間N ...中使用之前聲明」。有實際上是在當前命名空間(B)中的operator<<,因此它將該運算符添加到其匹配列表。 B中沒有更多的匹配項,並且由於同名空間作用域被認爲是最佳可能的匹配關係,所以它不會查看任何其他作用域。

當你把操作員到命名空間的它工作的原因是因爲被印在產品的A一員,該命名空間實際上被認爲是因爲它包含在表達的命名空間。由於名稱空間A認爲它在該名稱空間中找到適當的匹配並正確編譯。

既然它有一個可能的運算符列表,它會嘗試對它們進行重載解析。不幸的是,在命名空間B中找到的唯一一個它認爲它不符合所需的參數。

一般而言,您應該將插入操作符放在與其操作的類相同的命名空間中。

+1

+1。此處還提到「Koenig Lookup」的重要性/作用! – Nawaz 2011-03-04 15:27:30

+1

是的,在命名空間B中有一個運算符<<,但它沒有正確的簽名,那麼爲什麼編譯器不在全局命名空間中查找?如果B :: operator <<不存在 – chataign 2011-03-04 15:41:08

+0

您將代碼放在名稱空間中是有原因的。我們並不想要與全局命名空間中的所有名稱發生衝突! – 2011-03-04 16:58:24

8

我的回答是非常相似的,但其他人特別是編譯器試圖找到A ::操作< <(),因爲它是在該命名空間的東西運行。如果你想調用的名稱空間外的一個,你可以明確地使用

::operator<<(std::cout, A::MyClass(); 

流暢的語法使用調用它,把它的命名空間。

5

該問題已在@Mark B. 的答案中得到解釋。以下問題解決了此問題。在要使用全局operator<<的命名空間,鍵入下面的代碼:

using ::operator<<; 

在OP的代碼示例該行代碼會去的位置沿着它聲明的其他代碼/定義operator<<namespace B

namespace B 
{ 
    struct MyClass {}; 

    std::ostream & operator<<(std::ostream & os, const B::MyClass &) 
     { os << "namespace B"; return os; } 

    using ::operator<<; 
} 
+0

我個人更傾向於不用通常與類定義一起拉入全局運算符,而只有在需要解析時(例如,在函數void Test()'之前)。當然,我們可能需要多次這樣做,仍然看起來更清潔。 – Aconcagua 2016-11-17 09:18:14