C ++命名空间别名和转发声明

时间:2010-06-12 00:14:29

标签: c++ namespaces forward-declaration

我正在使用一个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图书馆。解决方案(至少在我的情况下)是对已接受答案的评论。

5 个答案:

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

答案 1 :(得分: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();
}

答案 2 :(得分:0)

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

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

答案 3 :(得分:0)

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

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

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

一个更安全的解决方案可以完全避免这个问题,只需使用您自己的命名空间即可。称之为tplib_ex或类似的东西,关联仍然是明确的,但冲突不会成为问题,您的别名相关问题也将消失。

答案 4 :(得分: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
相关问题