9

我正在使用一个 C++ 第三方库,它将其所有类都放在版本化命名空间中,我们称之为tplib_v44. 他们还定义了一个通用的命名空间别名:

namespace tplib = tplib_v44;

如果使用通用命名空间在我自己的 .h 文件中前向声明库的成员...

namespace tplib { class SomeClassInTpLib; }

...我在第三方库的头文件中出现编译器错误(稍后将包含在我的 .cpp 实现文件中):

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

如果我使用特定于版本的命名空间,那么一切正常,但是……有什么意义呢?处理这个问题的最佳方法是什么?

[编辑]仅供未来观众参考,这是 ICU 图书馆。一个解决方案(至少在我的情况下)在对已接受答案的评论中。

4

5 回答 5

4

看起来有一个丑陋的解决方法,但没有好的解决方案。

对于ACE(带有不错的解释)Xerces(带有尖刻的“这就是 c++ 的工作原理”评论),它们定义了可以用来“一般”执行此操作的宏。

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为命名空间,因此当编译器稍后尝试分配别名时,它不能同时是两者,因此编译器会抱怨。

于 2010-06-12T00:34:15.657 回答
2

我认为您的问题是由于 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();
}
于 2010-06-12T01:05:39.190 回答
0

呃……你在说什么在我看来是倒退的。恰恰相反,尝试将您的类声明为tplib命名空间的成员有什么意义?(暂时忘记它甚至不是名称空间,而是名称空间别名,这就是您收到错误的原因。)

很明显,您有某种基于命名空间和命名空间别名的版本控制系统。如果您的类首先在命名空间的某个特定“版本”(如 44)中引入 - 那就是它必须在其中声明的命名空间。你为什么要试图将你的类声明“及时”推到所有过去的版本中命名空间的名称(例如 43 和 30)?你的类在以前的版本中不存在,所以你不应该强迫它在那里。

于 2010-06-12T00:49:47.607 回答
0

编辑:我已经指出我错过了问题的重点-请随意忽略!

除了其他答案中突出显示的问题外,我还担心您首先尝试将自己的代码添加到第三方定义的命名空间中。

命名空间的存在是为了防止冲突的符号(类、类型定义、枚举等)发生冲突,方法是将它们放在自己的命名空间中,从而从可能相同的部分限定符号中开发出唯一的完全限定符号。将您自己的代码添加到第三方的命名空间可能会导致问题,如果(例如)在更高版本中他们决定他们也想使用相同的符号(比如添加自己的SomeClassInTpLib) - 突然之间,命名冲突命名空间旨在防止会抬起他们丑陋的脑袋。这就是为什么添加到std命名空间通常是不好的做法。

完全避免该问题的更安全的解决方案是简单地使用您自己的命名空间。调用它tplib_ex或类似的东西,关联仍然清晰,但冲突不会成为问题,您的别名相关问题也会消失。

于 2010-06-12T01:17:08.833 回答
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 个文件中应用 1 个更改。许多程序员已经将前向声明保留在某一点上,因为这样更易于管理,并且如果您必须前向声明很多东西,您可以使其他标头更清晰、更易读

#include <TpLibForwards.hpp> // my forwards declarations
于 2016-11-17T08:51:11.533 回答