C ++名称空间别名和前向声明

C++ namespace alias and forward declaration

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

1
namespace tplib = tplib_v44;

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

1
namespace tplib { class SomeClassInTpLib; }

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

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

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

[编辑]供以后观看者参考,这是ICU库。 解决方案(至少在我看来是这样)是在已接受答案的注释中。


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

对于ACE(带有恰当的解释)和Xerces(带有尖刻的"这就是c ++的工作方式"注释),它们定义了宏,您可以使用这些宏来"一般"地进行操作。

1
2
3
4
5
6
7
8
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声明为名称空间,因此,当编译器稍后尝试分配别名时,不能同时使用别名,因此编译器会抱怨。


我认为您的问题是由于tplib是别名而不是真实的名称空间

由于版本控制位于第三方库中,因此您可能无法使用它,但是在无版本名称空间中使用版本化名称空间(而不是对其进行别名)似乎适用于g ++ 4.0.1和4.1.2。但是我有种感觉,这不应该起作用……也许还有其他一些我不知道的问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//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();
}


what's the point?

阻止您在其名称空间中添加其他内容,可能是因为他们认为很快就会添加更多名称(并且他们正在使用版本化名称空间的事实可能暗示了这一点)。但是,这些只是推测。这具有防止您认为更合理的名称空间中的前向声明的副作用,因此我认为这只是不好的编程习惯。

What's the best way to deal with this?

没有最好的方法,但是要尽量避免使用宏,宏很难看,而且不好看(我不喜欢所有大写的东西)。如果您担心"更改版本时会发生什么?" (是的,理论上,您必须在所有代码中将" v44"更改为" v45")

然后只需使用1个标头来转发声明您需要的所有内容。

TpLibForwards.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#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个更改。许多程序员已经将向前声明保持在某一点上,因为这更易于管理,并且如果您必须向前声明很多内容,则可以使其他标头更整洁并更具可读性。

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

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

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

存在命名空间来防止冲突的符号(类,typdef,枚举等)冲突,方法是将它们放在自己的命名空间中,从而从可能相同的部分合格符号中开发出一个唯一的完全合格符号。如果(例如)在更高版本中第三方将自己的代码添加到第三方的命名空间中,而他们决定也想使用相同的符号(例如添加自己的SomeClassInTpLib),则会导致名称冲突。意味着要防止抬起丑陋的头。这就是为什么添加到std名称空间通常是不好的做法的原因。

可以完全避免该问题的更安全的解决方案是仅使用您自己的名称空间。将其命名为tplib_ex或类似名称,关联仍然清晰可见,但是冲突不会成为问题,并且与别名相关的问题也会消失。


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

显而易见,您已经建立了某种基于名称空间和名称空间别名的版本控制系统。如果您的类是在名称空间的某个特定"版本"(例如44)中首次引入的,那就是它必须在其中声明的名称空间。您为什么要尝试将类声明"倒推"到过去的所有版本中,名称空间的名称(例如43,例如30)?您的班级在以前的版本中不存在,因此您不应在此处强加它。