有没有一种更好的方式来expression嵌套在C ++中的名称空间
我从C ++切换到Java和C#,并认为名称空间/包的使用在那里(结构良好)要好得多。 然后,我回到C ++,并尝试以相同的方式使用名称空间,但在头文件中所需的语法是可怕的。
namespace MyCompany { namespace MyModule { namespace MyModulePart //eg Input { namespace MySubModulePart { namespace ... { public class MyClass
以下对我来说似乎也很奇怪(为了避免深度缩进):
namespace MyCompany { namespace MyModule { namespace MyModulePart //eg Input { namespace MySubModulePart { namespace ... { public class MyClass {
有没有更短的方式来expression上述的东西? 我缺less类似的东西
namespace MyCompany::MyModule::MyModulePart::... { public class MyClass
更新
好的,有人说在Java / C#和C ++中使用的概念是不同的。 真? 我认为(dynamic)类加载不是命名空间的唯一目的(这是一个非常技术性的理由的angular度)。 为什么我不应该使用它来实现可读性和结构化,比如想一想“智能感知”。
目前,名称空间和可以在其中find的内容之间没有逻辑/粘合。 Java和C#做的更好…为什么包括<iostream>
和名称空间std
? 好的,如果你说逻辑应该依赖头文件,那么为什么#include不使用像#include <std::io::stream>
或者<std/io/stream>
这样的“IntelliSense”友好语法? 我认为在缺省库中缺less结构化是C ++与Java / C#相比的一个弱点。
如果唯一性冲突是一个点(这也是C#和Java的一个点),一个好主意是使用项目名称或公司名称作为命名空间,你不这么认为吗?
一方面它说C ++是最灵活的…但是每个人都说“不要这样做”? 在我看来,C ++可以做许多事情,但是与C#相比,在许多情况下,即使是最简单的事情也有可怕的语法。
更新2
大多数用户认为创build比两个级别更深的嵌套是无稽之谈。 好的,Win8开发中的Windows :: UI :: Xaml和Windows :: UI :: Xaml :: Controls :: Primitives命名空间怎么样? 我认为微软对命名空间的使用是有道理的,而且它的确比2层次更深。 我认为更大的图书馆/项目需要更深的嵌套(我讨厌像ExtraLongClassNameBecauseEveryThingIsInTheSameNameSpace类名称…那么你可以把所有东西都放到全局命名空间)。
更新3 – 结论
大多数人说“不要这样做”,但是……即使是提升,也会有一两个层次的深层嵌套。 是的,这是一个图书馆,但是:如果你想要可重复使用的代码 – 把你自己的代码当作图书馆,你可以给别人。 我还使用命名空间来进行更深层的嵌套发现。
C ++ 17可能会简化嵌套的名称空间定义:
namespace A::B::C { }
相当于
namespace A { namespace B { namespace C { } } }
请参阅cppreference命名空间页上的(8):
http://en.cppreference.com/w/cpp/language/namespace
为了避免深度缩进,我通常这样做:
namespace A { namespace B { namespace C { class X { // ... }; }}}
C ++命名空间被用来对接口进行分组,而不是划分组件或表示政治划分。
这个标准不再采用类似于Java的命名空间的方式。 例如, 名称空间别名提供了一种轻松使用深层嵌套或长名称空间名称的方法。
namespace a { namespace b { namespace c {} } } namespace nsc = a::b::c;
但是namespace nsc {}
将会是一个错误,因为命名空间只能使用其原始名称空间名来定义。 从本质上来说,这个标准对于这样一个图书馆的使用者来说很容易,但是对于实施者来说很难。 这阻止了人们写这样的事情,但如果他们这样做,减轻了影响。
每个接口应该有一个名称空间,由一组相关的类和函数定义。 内部或可选的子接口可能会进入嵌套的命名空间。 但是两级以上的水平应该是一个非常严重的红旗。
考虑使用不需要::
运算符的下划线字符和标识符前缀。
不,请不要这样做。
命名空间的目的主要是解决全局命名空间中的冲突。
次要目的是符号的局部缩写; 例如复杂的UpdateUI
方法可能会使用using namespace WndUI
来使用较短的符号。
我在一个1.3MLoc项目,我们有唯一的命名空间是:
- 导入的外部COM库(主要是为了隔离
#import
和#include windows.h
之间的头文件冲突) - 某些方面(UI,DB访问等)的一个“公共API”命名空间级别
- 不属于公共API(“.cpp”中的匿名命名空间,或仅限Header的库中的“
ModuleDetailHereBeTygers
”命名空间)的“实现细节” - 枚举是我的经验中最大的问题。 他们疯狂污染。
- 我仍然觉得这是太多的命名空间
在这个项目中,类名称等使用两个或三个字母的“区域”代码(例如CDBNode
而不是DB::CNode
)。 如果你更喜欢后者,那么还有第二级的“公共”命名空间的空间,但没有更多。
特定于类的枚举等可以是这些类的成员(尽pipe我认为这并不总是好的,有时候很难说是否应该)
很less需要“公司”命名空间,除非您的第三方库以二进制forms存在很大的问题,不提供它们自己的命名空间,并且不能很容易地放入其中(例如在一个二进制分配)。 不过,以我的经验强迫他们进入命名空间要容易得多。
根据Stegi的后续问题:
好的,Win8开发中的Windows :: UI :: Xaml和Windows :: UI :: Xaml :: Controls :: Primitives命名空间怎么样? 我认为微软对命名空间的使用是有道理的,而且它的确比2层次更深
对不起,如果我不够清楚:两个层次不是硬性的限制,更多的不是固有的坏。 我只想指出,从我的经验来看,甚至在一个庞大的代码库上,你也很less需要两个以上的代码。 嵌套更深或更浅是一个折衷。
现在,微软的情况可能有所不同。 据推测是一个更大的团队, 所有的代码是图书馆。
我假设微软在这里模仿.NET库的成功,在这个库中,名字空间有助于扩展库的可发现性。 (.NET有大约18000个types。)
我进一步假设在命名空间中有一个最佳的(数量级的)符号。 比如说,1听起来没有道理,100听起来很对,万头清楚。
TL; DR:这是一个权衡,我们没有硬数字。 玩得安全,不要向任何方向过度。 “不这样做”只是来自“你有问题,我会遇到问题,我不明白为什么你需要它”。
我完全支持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
这也消除了命名空间的左括号附近的注释需要(你有没有向下滚动到一个大的源文件的底部,并试图添加/删除/平衡大括号,哪些大括号closures哪个范围缺less评论? )。
MY_COMPANY_BEGIN MY_LIBRARY_BEGIN class X { }; class Y { }; MY_LIBRARY_END MY_COMPANY_END
如果你想把所有的命名空间声明放在一行上,你可以用一些(非常丑陋的)预处理器来实现:
// 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__)
现在你可以做到这一点:
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;
对于比三层更深的嵌套,您将不得不添加助手macros达到所需的计数。
这里引用Lzz (Lazy C ++)的文档:
Lzz识别下面的C ++结构:
命名空间定义
一个未命名的名称空间和所有封闭的声明被输出到源文件。 这个规则覆盖所有其他的。
命名的命名空间的名称可能是合格的。
namespace A::B { typedef int I; }
相当于:
namespace A { namespace B { typedef int I; } }
当然,依赖于这些工具的来源的质量是值得商榷的…我会说这更多的是好奇心,表明由C ++引发的语法疾病可以采取多种forms(我也有我的…)
这两个标准(C ++ 2003和C ++ 11)都非常明确,名称空间的名称是一个标识符。 这意味着需要明确的嵌套标头。
我的印象是,除了名称空间的简单名称之外,允许放置合格的标识符并不是什么大事,但由于某些原因,这是不允许的。
是的,你将不得不这样做
namespace A{ namespace B{ namespace C{} } }
但是,您正尝试以不应该使用的方式使用名称空间。 检查这个问题,也许你会发现它有用。