2010-06-12 93 views
7

我正在使用C++第三方庫,將其所有類放入版本化命名空間中,我們將其稱爲tplib_v44。它們還定義了一個通用的命名空間別名:C++名稱空間別名和前向聲明

namespace tplib = tplib_v44; 

如果向前聲明中使用通用命名空間我自己的.h文件庫中的一員......

namespace tplib { class SomeClassInTpLib; } 

...我得到在第三方庫的頭編譯器錯誤(這是後來被包含在我的.cpp實現文件):

error C2386: 'tplib' : a symbol with this name already exists in the current scope 

如果我使用特定版本的命名空間,那麼一切工作正常,但後來.. w帽子的重點?處理這個問題的最好方法是什麼?

[編輯]供未來觀衆參考,這是ICU圖書館。解決方案(至少在我的情況下)是在對接受答案的評論中。

回答

4

它看起來像有一個醜陋的解決方法,但沒有好的解決方案。

對於ACE (with a decent explanation)Xerces (with a snarky "this is how c++ works" comment),他們定義了可以用來做這個「一般」的宏。

ACE_BEGIN_VERSIONED_NAMESPACE_DECL 
class ACE_Reactor; 
ACE_END_VERSIONED_NAMESPACE_DECL 

XERCES_CPP_NAMESPACE_BEGIN 
class DOMDocument; 
class DOMElement; 
XERCES_CPP_NAMESPACE_END 

它看起來像一個不幸的C++神器,嘗試在tplib這些宏摸索。

該標準將名稱空間和名稱空間別名視爲不同的事物。你聲明tplib是一個命名空間,所以當編譯器試圖稍後分配一個別名時,它不能同時存在,所以編譯器會抱怨。

+0

這非常接近爲我工作的結果。事實證明,我的圖書館(ICU圖書館)有一個小頭(uversion.h),除其他外,它定義了名稱空間別名。如果我將這個頭文件包含在頭文件中,我可以使用他們在頭文件中定義的版本化命名空間宏(U_ICU_NAMESPACE),並在我的cpp文件中使用通用名稱空間。因此,我無需修改版本化的名稱空間,只需最小的編譯時間依賴性。 – 2010-06-14 13:38:10

0

呃...你在說什麼向後看我。恰恰相反,試圖將你的類聲明爲tplib命名空間的成員有什麼意義? (忘了秒,它甚至不是一個命名空間,而是一個命名空間別名,這就是爲什麼你會得到錯誤。)

很明顯,你有一些版本控制系統建立在命名空間和命名空間別名。如果你的類是第一次引入某個名字空間的特定「版本」(比如44) - 那就是它必須聲明的名字空間。你爲什麼試圖推遲你的類聲明「回到時間」,即進入全部命名空間的過去版本(比如43和30)?你的課程在以前的版本中不存在,所以你不應該在那裏強制它。

+1

我想我的原始問題並不清楚。命名空間別名由第三方開發者提供,以便我可以將它們的類稱爲「tplib :: SomeClass」。他們在內部使用版本化命名空間。所以在他們的下一個版本中,內部命名空間可能是'tplib_v50'。但是我不需要瀏覽所有的文件,因爲我正在使用別名,因此將'tplib_v44 ::'更改爲'tplib_v50 ::'。我沒有在名稱空間中聲明我的類;只是在我的類的頭文件中向前聲明一個來自該庫的類。希望這是有道理的。 – 2010-06-14 13:27:56

1

我覺得你的問題是由於tplib是一個別名,而不是一個真正的命名空間

由於該版本是一個第三方庫裏面,你可能無法使用它,但使用內未版本化的版本命名空間命名空間(而不是別名)似乎適用於g ++ 4.0.1和4.1.2。不過,我有一種感覺,這不應該工作...也許還有一些我不知道的其他問題。

//This is the versioned namespace 
namespace tplib_v44 
{ 
    int foo(){ return 1; } 
} 

//An unversioned namespace using the versioned one 
namespace tplib 
{ 
    using namespace tplib_v44; 
} 


//Since unversioned is a real namespace, not an alias you can add to it normallly. 
namespace tplib 
{ 
    class Something {}; 
} 


int main() 
{ 
    //Just to make sure it all works as expected 
    tplib::foo(); 
} 
+0

只要注意,如果已經在版本化的命名空間中聲明瞭'Something',這將不會引發錯誤。它會默默接受並且'tplib :: Something'會引用版本化的,忽略'tplib :: Something'。此外,ADL不會以'Something'作爲參數進行調用 - 版本化的名稱空間將不會被查找。 – 2010-06-13 09:20:01

+0

@Johannes - 聽起來很合理。你能舉一個ADL如何在使用情況下失敗並且不是直接命名空間情況的例子嗎? – 2010-06-13 10:12:35

0

編輯:我已經受夠指出,我已經錯過了問題的要點 - 請隨時無視!

除了其他答案中突出顯示的問題之外,它還讓我擔心你有一點想要將自己的代碼添加到第三方定義的名稱空間中。

命名空間的存在是爲了防止衝突的符號(類,typdefs,枚舉等)發生衝突,方法是將它們放置在它們自己的命名空間中,從而從可能相同的部分合格的符號開發出一個唯一的完全限定符號。將自己的代碼添加到第三方的命名空間可能會導致問題,例如,如果(例如)在更高版本中他們決定他們也想使用相同的符號(如添加自己的SomeClassInTpLib) - 突然間,命名衝突命名空間意味着防止他們醜陋的後腦袋。這就是爲什麼添加到std命名空間通常是不好的做法。

完全避免此問題的更安全的解決方案是簡單地使用自己的名稱空間。把它稱爲tplib_ex或類似的東西,這個協會仍然是清楚的,但衝突不會是一個問題,你的別名相關問題也將消失。

+0

他沒有向庫中添加符號,他試圖轉發已存在於第三方名稱空間(別名)內的聲明對象。 – Stephen 2010-06-12 01:33:04

+0

啊,的確如此。不知怎的,錯過了。感謝您的更正。 – Mac 2010-06-12 04:22:22

0

有什麼意義?

從他們的命名空間添加額外的東西,也許是因爲他們認爲儘快增加更多的名字(他們使用的版本命名空間的事實可能會建議)防止你。然而,這些只是猜測。這具有阻止你認爲更合理的命名空間中的前向聲明的副作用,所以我認爲這只是糟糕的編程習慣。

解決這個問題的最佳方法是什麼?

有沒有最好的辦法,但儘量避免使用宏,宏是醜陋的,不好看(我不喜歡所有大寫的東西)。如果你關心的是「當他們改變版本時會發生什麼?」 (是的理論上你必須在所有代碼中將「v44」更改爲「v45」)

然後只需使用1個單一標題即可聲明所需的所有內容。

TpLibForwards.hpp

#ifdef XXXXXX_TPLIB 
    #error "XXXXXX_TPLIB is already taken, change to something else" 
#endif 
#define XXXXXX_TPLIB tplib_v44 
//... and that's why I don't like keeping macros around.. 

namespace XXXXXX_TPLIB 
{ 

    // FORWARD DECLARATIONS 
    class A1; 
    class A2; 
    //... 
} 
namespace tplib = XXXXXX_TPLIB; 
#undef XXXXXX_TPLIB 

如果他們改變了庫,那麼你只需要申請1種變化,在一個文件中。許多程序員已經在一個點上保留了聲明,因爲這更容易管理,並且如果你不得不轉發聲明很多你保留的東西其他頭文件更乾淨並且更易讀

#include <TpLibForwards.hpp> // my forwards declarations