是否有更好的方法在标头中用C++表示嵌套命名空间

Bea*_*ker 85 c++ syntax namespaces

我从C++切换到Java和C#,并认为命名空间/包的使用在那里(结构良好)更好.然后我回到C++并试图以相同的方式使用命名空间,但在头文件中所需的语法是可怕的.

namespace MyCompany
{
    namespace MyModule
    {
        namespace MyModulePart //e.g. Input
        {
            namespace MySubModulePart
            {
                namespace ...
                {
                    public class MyClass    
Run Code Online (Sandbox Code Playgroud)

以下内容对我来说也很奇怪(避免深度缩进):

namespace MyCompany
{
namespace MyModule
{
namespace MyModulePart //e.g. Input
{
namespace MySubModulePart
{
namespace ...
{
     public class MyClass
     {
Run Code Online (Sandbox Code Playgroud)

是否有更短的方式来表达上述事情?我错过了类似的东西

namespace MyCompany::MyModule::MyModulePart::...
{
   public class MyClass
Run Code Online (Sandbox Code Playgroud)

更新

好吧,有人说Java/C#和C++中的使用概念是不同的.真?我认为(动态)类加载不是命名空间的唯一目的(这是一个非常技术性的推理视角).为什么我不应该将它用于可读性和结构化,例如想到"IntelliSense".

目前,命名空间与您可以在其中找到的内容之间没有逻辑/粘合剂.Java和C#做得更好......为什么要包含<iostream>和拥有命名空间std?好吧,如果你说的逻辑应该依靠头部以包括,为什么#包括不使用的"智能感知"友好的语法像#include <std::io::stream><std/io/stream>?我认为与Java/C#相比,缺省库中缺少的结构化是C++的一个弱点.

如果狂热冲突的唯一性是一个Point(这也是C#和Java的一个点),一个好主意是使用项目名称或公司名称作为命名空间,你不这么认为吗?

一方面它说C++是最灵活的...但每个人都说"不要这样做"?在我看来,C++可以做很多事情,但是在很多情况下,与C#相比,即使是最简单的事情也会有一种可怕的语法.

更新2

大多数用户认为创建比两个级别更深的嵌套是无稽之谈.好的,那么Win8开发中的Windows :: UI :: Xaml和Windows :: UI :: Xaml :: Controls :: Primitives名称空间呢?我认为微软对名称空间的使用是有道理的,它确实比仅仅2级更深.我认为更大的库/项目需要更深层次的嵌套(我讨厌像ExtraLongClassNameBecauseEveryThingIsInTheSameNameSpace这样的类名...那么你也可以将所有东西放到全局命名空间中.)

更新3 - 结论

大多数人说"不要这样做",但是......甚至提升都有一个更深的嵌套,然后是一个或两个级别.是的,它是一个库,但是:如果你想要可重复使用的代码 - 将你自己的代码视为一个库你可以给别人.我还使用更深层次的嵌套来使用命名空间进行发现.

W1M*_*M0R 115

C++ 17可能会简化嵌套的命名空间定义:

namespace A::B::C {
}
Run Code Online (Sandbox Code Playgroud)

相当于

namespace A { namespace B { namespace C {
} } }
Run Code Online (Sandbox Code Playgroud)

有关cppreference的命名空间页面,请参阅(8):http:
//en.cppreference.com/w/cpp/language/namespace

  • ...由编译器开关`/ std:c ++ latest`启用 (4认同)
  • 请注意,如果您在Visual Studio 2015中使用`/ std:c ++ latest`并使用Boost,则在包含一些Boost标头时可能会遇到非常神秘的编译器错误.我在[StackOverflow问题]中描述了这个问题(http://stackoverflow.com/questions/40108603/c2143-syntax-error-when-including-boost-optional-hpp) (2认同)
  • 它按原样工作,命名空间 A::B::C。我已经用 g++-6.0 进行了测试 (2认同)

Kur*_*son 27

为了避免真正的深度缩进,我通常这样做:

namespace A { namespace B { namespace C
{
    class X
    {
        // ...
    };
}}}
Run Code Online (Sandbox Code Playgroud)

  • JFYI `clang-format` 无法在您显示 http://clang.llvm.org/docs/ClangFormatStyleOptions.html (NamespaceIndentation) 时对其进行格式化 (3认同)

Pot*_*ter 13

C++命名空间用于分组接口,而不是划分组件或表达政治分裂.

该标准禁止使用类似Java的命名空间.例如,命名空间别名提供了一种轻松使用深层嵌套或长命名空间名称的方法.

namespace a {
namespace b {
namespace c {}
}
}

namespace nsc = a::b::c;
Run Code Online (Sandbox Code Playgroud)

但是namespace nsc {}这将是一个错误,因为命名空间只能使用其原始名称空间名称来定义.基本上,标准使这样的库的用户容易,但实施者很难.这会阻止人们写这些东西,但如果他们这样做,就会减轻影响.

每个接口应该有一个命名空间,由一组相关的类和函数定义.内部或可选的子接口可能会进入嵌套的命名空间.但超过两个级别应该是一个非常严重的危险信号.

考虑使用::不需要运算符的下划线字符和标识符前缀.

  • 好的,那么Win8开发中的Windows :: UI :: Xaml和Windows :: UI :: Xaml :: Controls :: Primitives名称空间呢?我认为微软对名称空间的使用是有道理的,它确实比仅仅2级更深. (13认同)
  • @Potatoswatter 关键是您实际上是免费获得它的(company::division 不比 company_division 长)并且不必先定义额外的命名空间别名来使用它。 (3认同)
  • 使用少于 2 个级别是一个危险信号,使用 3 或 4 级完全没问题。试图在没有意义的情况下实现扁平的命名空间层次结构违背了命名空间的真正目的——避免名称冲突。我同意您应该为接口设置一个级别,为子接口和内部组件设置另一个级别。但是在这方面,您至少需要为公司命名空间(对于中小型公司)或公司和部门(对于大公司)两个级别。否则,您的接口名称空间将与其他地方开发的具有相同名称的其他接口的名称空间发生冲突 (2认同)

Max*_*uxa 13

我完全支持peterchen的回答,但想要添加一些解决你问题另一部分的内容.

声明命名空间是C++中非常罕见的情况之一,我实际上喜欢使用#defines.

#define MY_COMPANY_BEGIN  namespace MyCompany { // begin of the MyCompany namespace
#define MY_COMPANY_END    }                     // end of the MyCompany namespace
#define MY_LIBRARY_BEGIN  namespace MyLibrary { // begin of the MyLibrary namespace
#define MY_LIBRARY_END    }                     // end of the MyLibrary namespace
Run Code Online (Sandbox Code Playgroud)

这也消除了命名空间右括号附近的注释需求(你是否曾向下滚动到一个大型源文件的底部,并尝试添加/删除/平衡缺少关于哪个括号关闭哪个范围的注释的大括号?不好玩).

MY_COMPANY_BEGIN
MY_LIBRARY_BEGIN

class X { };

class Y { };

MY_LIBRARY_END
MY_COMPANY_END
Run Code Online (Sandbox Code Playgroud)

如果你想将所有名称空间声明放在一行上,你也可以使用一些(非常难看的)预处理器魔法:

// helper macros for variadic macro overloading
#define VA_HELPER_EXPAND(_X)                    _X  // workaround for Visual Studio
#define VA_COUNT_HELPER(_1, _2, _3, _4, _5, _6, _Count, ...) _Count
#define VA_COUNT(...)                           VA_HELPER_EXPAND(VA_COUNT_HELPER(__VA_ARGS__, 6, 5, 4, 3, 2, 1))
#define VA_SELECT_CAT(_Name, _Count, ...)       VA_HELPER_EXPAND(_Name##_Count(__VA_ARGS__))
#define VA_SELECT_HELPER(_Name, _Count, ...)    VA_SELECT_CAT(_Name, _Count, __VA_ARGS__)
#define VA_SELECT(_Name, ...)                   VA_SELECT_HELPER(_Name, VA_COUNT(__VA_ARGS__), __VA_ARGS__)

// overloads for NAMESPACE_BEGIN
#define NAMESPACE_BEGIN_HELPER1(_Ns1)             namespace _Ns1 {
#define NAMESPACE_BEGIN_HELPER2(_Ns1, _Ns2)       namespace _Ns1 { NAMESPACE_BEGIN_HELPER1(_Ns2)
#define NAMESPACE_BEGIN_HELPER3(_Ns1, _Ns2, _Ns3) namespace _Ns1 { NAMESPACE_BEGIN_HELPER2(_Ns2, _Ns3)

// overloads for NAMESPACE_END
#define NAMESPACE_END_HELPER1(_Ns1)               }
#define NAMESPACE_END_HELPER2(_Ns1, _Ns2)         } NAMESPACE_END_HELPER1(_Ns2)
#define NAMESPACE_END_HELPER3(_Ns1, _Ns2, _Ns3)   } NAMESPACE_END_HELPER2(_Ns2, _Ns3)

// final macros
#define NAMESPACE_BEGIN(_Namespace, ...)    VA_SELECT(NAMESPACE_BEGIN_HELPER, _Namespace, __VA_ARGS__)
#define NAMESPACE_END(_Namespace, ...)      VA_SELECT(NAMESPACE_END_HELPER,   _Namespace, __VA_ARGS__)
Run Code Online (Sandbox Code Playgroud)

现在你可以这样做:

NAMESPACE_BEGIN(Foo, Bar, Baz)

class X { };

NAMESPACE_END(Baz, Bar, Foo) // order doesn't matter, NAMESPACE_END(a, b, c) would work equally well

Foo::Bar::Baz::X x;
Run Code Online (Sandbox Code Playgroud)

对于嵌套深度超过三个级别,您必须将助手宏添加到所需的计数.

  • 尽管我不喜欢“#define”,但预处理器的魔力给我留下了深刻的印象...除非我不必添加额外的帮助宏来进行更深层次的嵌套...好吧,我不会使用它无论如何所以... (2认同)

pet*_*hen 8

不,请不要那样做.

命名空间的目的主要是解决全局命名空间中的冲突.

次要目的是符号的局部缩写; 例如,复杂UpdateUI方法可以使用a using namespace WndUI来使用较短的符号.

我正在使用1.3MLoc项目,我们唯一的命名空间是:

  • 导入外部COM库(主要用于隔离#import和之间的头冲突 #include windows.h)
  • 某些方面的一级"公共API"命名空间(UI,数据库访问等)
  • 不属于公共API的"实现详细信息"命名空间(.cpp中的匿名命名空间或ModuleDetailHereBeTygers仅限标题库中的命名空间)
  • 枚举是我经历中最大的问题.他们像疯了一样污染.
  • 我仍然觉得它的命名空间太多了

在这个项目中,类名等使用两个或三个字母的"区域"代码(例如CDBNode代替DB::CNode).如果您更喜欢后者,则可以使用第二级"公共"命名空间,但不能再使用.

特定于类的枚举等可以是这些类的成员(虽然我同意这并不总是好的,有时很难说你是否应该这样做)

很少需要一个"公司"命名空间,除非你遇到了以二进制形式分发的第三方库的大问题,不提供自己的命名空间,并且不能轻易地放入一个(例如二进制文件中)分配).不过,根据我的经验,强迫他们进入命名空间更容易.


[编辑]根据Stegi的后续问题:

好的,那么Win8开发中的Windows :: UI :: Xaml和Windows :: UI :: Xaml :: Controls :: Primitives名称空间呢?我认为微软对名称空间的使用是有道理的,它确实比仅仅2级更深

对不起,如果我不够清楚:两个级别不是硬限制,更多不是本质上不好.我只想指出,根据我的经验,即使是在大型代码库中,您也很少需要两个以上.更深或更浅的嵌套是一种权衡.

现在,微软的案例可以说是不同的.大概是一个更大的团队,所有代码都是库.

我假设微软在这里模仿.NET库的成功,其中命名空间有助于扩展库的可发现性.(.NET有大约18000种类型.)

我进一步假设命名空间中存在最佳(数量级)符号.比如说,1没有意义,100个声音正确,10000个显然是很多.


TL; DR:这是一个权衡,我们没有硬数字.玩安全,不要在任何方向过度."不要那样做"只来自"你有问题,我有问题,我没有理由你需要它."

  • 好的,那么Win8开发中的Windows :: UI :: Xaml和Windows :: UI :: Xaml :: Controls :: Primitives名称空间呢?我认为微软对名称空间的使用是有道理的,它确实比仅仅2级更深. (7认同)
  • 如果我需要全局访问常量,我喜欢将它们放在一个名称为“Constants”的命名空间中,然后创建具有适当名称的嵌套命名空间来对常量进行分类;如有必要,我会使用更多的命名空间来防止名称冲突。这个 `Constants` 命名空间本身包含在程序系统代码的一个包罗万象的命名空间中,其名称如 `SysData`。这将创建一个包含三个或四个命名空间的完全限定名称(例如 `SysData::Constants::ErrorMessages`、`SysData::Constants::Ailments::Bitflags` 或 `SysData::Defaults::Engine::TextSystem `)。 (2认同)
  • 总的来说,我会说在一般情况下,最好使用最少数量的嵌套命名空间,但如果由于某种原因需要全局对象(无论是常量还是可变,最好是前者),应该使用多个嵌套命名空间将它们分成适当的类别,以记录它们的使用并最小化潜在的名称冲突. (2认同)
  • -1表示“请不要这样做”,而没有*客观*的原因(尽管稍后进行了说明)。该语言支持嵌套名称空间,因此项目可能有充分的理由使用它们。讨论可能的此类原因以及这样做的任何*具体,客观*的弊端,将使我的观点下降。 (2认同)

Cap*_*liC 5

这里引用了Lzz (Lazy C++) 文档:

Lzz 识别以下 C++ 结构:

命名空间定义

未命名的命名空间和所有封闭的声明都输出到源文件。此规则覆盖所有其他规则。

命名空间的名称可以被限定。

   namespace A::B { typedef int I; }
Run Code Online (Sandbox Code Playgroud)

相当于:

   namespace A { namespace B { typedef int I; } }
Run Code Online (Sandbox Code Playgroud)

当然,依赖于这些工具的源代码的质量是有争议的......我会说这更像是一种好奇心,表明由 C++ 引起的语法疾病可以有多种形式(我也有我的......)