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

我从 C + + 切换到 Java 和 C # ,认为那里名称空间/包的使用更好(结构良好)。然后我回到 C + + ,尝试以同样的方式使用名称空间,但是所需的语法在头文件中非常糟糕。

namespace MyCompany
{
namespace MyModule
{
namespace MyModulePart //e.g. Input
{
namespace MySubModulePart
{
namespace ...
{
public class MyClass

下面这些对我来说似乎也很奇怪(为了避免深刻的缩进) :

namespace MyCompany
{
namespace MyModule
{
namespace MyModulePart //e.g. Input
{
namespace MySubModulePart
{
namespace ...
{
public class MyClass
{

有没有一个更简短的方法来表达上面的东西? 我错过了一些东西,比如

namespace MyCompany::MyModule::MyModulePart::...
{
public class MyClass

更新

好吧,有人说 Java/C # 和 C + + 的使用概念是不同的。真的吗?我认为(动态)类加载并不是名称空间的唯一目的(这是一个非常技术性的观点)。为什么我不能用它来提高可读性和结构化,例如“智能感知”。

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

如果避免冲突的唯一性是一点(这也是 C # 和 Java 的一点) ,那么最好使用项目名或公司名作为名称空间,您不这样认为吗?

一方面,人们说 C + + 是最灵活的... ... 但是每个人都说“不要这样做”? 在我看来,与 C # 相比,C + + 可以做很多事情,但是它的语法非常糟糕,即使对于很多情况下最简单的事情也是如此。

更新2

Most users say it is nonsense to create a deeper nesting than two Levels. Ok, so what about Windows::UI::Xaml and Windows::UI::Xaml::Controls::Primitives namespaces in Win8 development? I think Microsoft's usage of namespaces makes sense and it is indeed deeper than just 2 Levels. 我认为更大的库/项目需要更深的嵌套(我讨厌像 ExtraLongClassNameBecuseEveryThingIsInTheSameName 空间这样的类名... ... 然后你也可以把所有东西放到全局名称空间中)

更新3-结论

大多数人说“不要这样做”,但是... 即使是升级也有一个比一两个层次更深的嵌套。是的,它是一个库,但是: 如果你想要可重用的代码——把你自己的代码当作一个库,你会把它给别人。我还使用了更深层的嵌套,以便使用名称空间进行发现。

78959 次浏览

Both standards (C++2003 and C++11) are very explicit that the name of the namespace is an identifier. This means that explicit nested headers are required.

我的印象是,除了名称空间的简单名称之外,允许放置限定标识符并没有什么大不了的,但由于某些原因,这是不允许的。

C + + 名称空间用于对接口进行分组,而不是划分组件或表达政治分歧。

该标准特意禁止像 Java 一样使用名称空间。例如,命名空间别名提供了一种方法,可以方便地使用深度嵌套或较长的名称空间名称。

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


namespace nsc = a::b::c;

但是 namespace nsc {}将是一个错误,因为名称空间可能只能使用它的 原始-命名空间-名称来定义。本质上,该标准使这样一个库的 使用者变得容易,但使 执行者变得困难。这使人们不愿意写这样的东西,但是如果他们这样做的话,影响就会减轻。

每个接口应该有一个由一组相关类和函数定义的名称空间。内部或可选的子接口可能进入嵌套名称空间。但是两层以上的深度应该是一个非常严重的危险信号。

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

是的,你必须这样做

namespace A{
namespace B{
namespace C{}
}
}

但是,您试图以一种不应该使用名称空间的方式来使用它们。检查 this问题,也许你会发现它有用。

以下是引自 Lzz(Lazy C + +)文档的一段话:

Lzz 识别以下 C + + 结构:

命名空间定义

未命名的命名空间和所有封闭的声明都会输出到源文件中。

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

   namespace A::B { typedef int I; }

等同于:

   namespace A { namespace B { typedef int I; } }

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

不,请不要这么做。

名称空间的用途主要是解决全局名称空间中的冲突。

A secondary purpose is local abbreviation of symbols; e.g. a complex UpdateUI method may use an using namespace WndUI to use shorter symbols.

I'm on a 1.3MLoc project, and the only namespaces we have are:

  • 导入的外部 COM 库(主要用于隔离 #import#include windows.h)
  • One level of "public API" namespaces for certain aspects (UI, DB access etc.)
  • "Implementation Detail" namespaces that are not part of the public API (. cpp 中的匿名名称空间,或者 header-only libs)
  • 枚举是我的经验中最大的问题。他们像疯了一样污染。
  • 我还是觉得名称空间太多了

在这个项目中,类名等等使用两个或三个字母的“区域”代码(例如 CDBNode而不是 DB::CNode)。如果您更喜欢后者,那么还可以使用第二级的“公共”名称空间,但仅此而已。

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

There's rarely need for a "company" namespace either, except if you are having big problems with 3rd party libraries that are distributed as binary, don't provide their own namespace, and can't be easily put into one (e.g. in a binary distribution). Still, in my experience forcing 他们 into a namespace is much easier to do.


根据 Stegi 的后续问题:

好的,那么在 Win8开发中使用 Windows: : UI: : Xaml 和 Windows: : UI: : Xaml: : Controls: : Primitive 名称空间怎么样?我认为微软对名称空间的使用是有意义的,它确实比2级更深入

对不起,我没有说清楚: 两个等级并不是一个硬性限制,更多的等级本质上并不坏。我只是想指出,根据我的经验,即使在大型代码库中,需要也很少超过两个。嵌套更深或更浅是一种权衡。

现在,微软的情况可以说是不同的。大概是一个更大的团队,和 所有的代码是库。

我认为微软是在模仿。NET 库,其中命名空间贡献了扩展库的 可发现性。(.NET 具有 大约18000年类型。)

I'd further assume that there is an optimal (order of magnitude of) symbols in a namespace. say, 1 doesn't make sense, 100 sounds right, 10000 is clearly to much.


这是一种权衡,我们没有确切的数字。注意安全,不要在任何方面做过头。“不要那样做”仅仅来自于“你对此有问题,我对此也有问题,我不知道你为什么需要它。”.

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

namespace A { namespace B { namespace C
{
class X
{
// ...
};
}}}

我完全支持 Peterchen 的回答,但是我想添加一些东西来解决你的问题的另一部分。

在 C + + 中,声明名称空间是我实际上喜欢使用 #define的非常少见的情况之一。

#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

这也消除了在名称空间的大括号附近添加注释的需要(您是否曾经向下滚动到一个大的源文件的底部,并试图添加/删除/平衡那些缺少关于哪个大括号关闭哪个作用域的注释的大括号?一点都不好玩。).

MY_COMPANY_BEGIN
MY_LIBRARY_BEGIN


class X { };


class Y { };


MY_LIBRARY_END
MY_COMPANY_END

If you want to put all namespace declarations on a single line you can do that as well with a bit of (pretty ugly) preprocessor magic:

// 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__)

Now you can do this:

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;

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

C + + 17可以简化嵌套的名称空间定义:

namespace A::B::C {
}

is equivalent to

namespace A { namespace B { namespace C {
} } }

请参见(8)关于 cpferences 的名称空间页面:
Http://en.cppreference.com/w/cpp/language/namespace

您可以使用以下语法:

namespace MyCompany {
namespace MyModule {
namespace MyModulePart //e.g. Input {
namespace MySubModulePart {
namespace ... {
class MyClass;
}
}
}
}
}


// Here is where the magic happens
class MyCompany::MyModule::MyModulePart::MySubModulePart::MyYouGetTheIdeaModule::MyClass {
...
};

注意,这种语法即使在 C + + 98中也是有效的,它几乎类似于现在 C + + 17中使用 嵌套命名空间定义的语法。

解套愉快!

资料来源:

[编辑: ]
由于 c + + 17嵌套名称空间作为标准语言特性(https://en.wikipedia.org/wiki/C%2B%2B17)受到支持。到目前为止,g + + 8还不支持这个特性,但是可以在 in clang + + 6.0编译器中找到它。


[ CONCLUSION: ]
使用 clang++6.0 -std=c++17作为默认编译命令。然后一切应该工作正常-你将能够编译与 namespace OuterNS::InnerNS1::InnerNS2 { ... }在您的文件。


[原答案: ]
既然这个问题有点老,我就假设你已经向前看了。但对于其他仍在寻找答案的人,我提出了以下想法:

Emacs buffers showing main file, namespace files, compilation command/result, and command-line execution.

(请允许我在这里为 Emacs 做一个广告:) ?) 发布图片比简单地发布代码要容易得多,也更易读。我不打算提供一个完整的答案所有角落的情况下,只是我想给一些灵感。(我完全支持 C # ,并且认为在很多情况下 C + + 应该采用一些 OOP 特性,因为 C # 之所以流行主要是因为它易于使用)。

本文相当好地涵盖了这一主题: 命名空间文件

基本上就是这样。名称空间越长,人们使用 using namespace指令的可能性就越大。

因此,看看下面的代码,你会发现一个例子,这将伤害你:

namespace abc { namespace testing {
class myClass {};
}}


namespace def { namespace testing {
class defClass { };
}}


using namespace abc;
//using namespace def;


int main(int, char**) {
testing::myClass classInit{};
}

但是,如果取消对 //using namespace def;行的注释,那么“测试”命名空间将变得模糊不清,并且会出现命名冲突,那么这段代码可以很好地编译。这意味着通过包含第三方库,代码库可以从稳定变为不稳定。

在 C # 中,即使使用 using abc;using def;,编译器也能够识别出 testing::myClass或者甚至仅仅是 myClass只存在于 abc::testing名称空间中,但是 C + + 不能识别这一点,并且会将其作为冲突检测出来。