有没有更好的方法在头文件中用 C++ 表达嵌套的命名空间
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11358425/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is there a better way to express nested namespaces in C++ within the header
提问by Beachwalker
I switched from C++ to Java and C# and think the usage of namespaces/packages is much better there (well structured). Then I came back to C++ and tried to use namespaces the same way but the required syntax is horrible within the header file.
我从 C++ 切换到 Java 和 C#,并认为命名空间/包在那里的使用要好得多(结构良好)。然后我回到 C++ 并尝试以相同的方式使用命名空间,但在头文件中所需的语法很糟糕。
namespace MyCompany
{
namespace MyModule
{
namespace MyModulePart //e.g. Input
{
namespace MySubModulePart
{
namespace ...
{
public class MyClass
The following seems strange to me too (to avoid the deep indent):
以下对我来说也很奇怪(以避免深度缩进):
namespace MyCompany
{
namespace MyModule
{
namespace MyModulePart //e.g. Input
{
namespace MySubModulePart
{
namespace ...
{
public class MyClass
{
Is there a shorter Way to express the above thing? I am missing something like
有没有更短的方式来表达以上内容?我错过了类似的东西
namespace MyCompany::MyModule::MyModulePart::...
{
public class MyClass
Update
更新
Ok, some say the concept of usage in Java/C# and C++ is different. Really? I think (dynamic) class loading is not the only purpose for namespaces (this is a very technical reasoned perspective). Why shouldn't I use it for a readability and structurization, e.g think of "IntelliSense".
好吧,有人说 Java/C# 和 C++ 中的用法概念是不同的。真的吗?我认为(动态)类加载不是命名空间的唯一目的(这是一个非常技术性的推理视角)。为什么我不应该使用它来提高可读性和结构化,例如考虑“IntelliSense”。
Currently, there is no logic / glue between a namespace and what you can find there. Java and C# does this much better... Why including <iostream>
and having namespace std
?
Ok, if you say the logic should rely on the header to include, why does the #include does not uses an "IntelliSense" friendly syntax like #include <std::io::stream>
or <std/io/stream>
? I think the missing structurization in the default libs is one weakness of C++ compared to Java/C#.
目前,命名空间和您在那里可以找到的内容之间没有逻辑/胶水。Java 和 C# 在这方面做得更好......为什么要包含<iostream>
和拥有命名空间std
?好的,如果您说逻辑应该依赖于标头来包含,为什么 #include 不使用“智能感知”友好语法,例如#include <std::io::stream>
或<std/io/stream>
?我认为默认库中缺少结构化是 C++ 与 Java/C# 相比的一个弱点。
If uniqueness to avid conflicts is one Point (which is a point of C# and Java, too) a good idea is to use the project name or company name as namespace, don't you think so?
如果狂热冲突的唯一性是一个点(这也是 C# 和 Java 的一个点),一个好主意是使用项目名称或公司名称作为命名空间,您不这么认为吗?
On the one hand it's said C++ is the most flexible... but everyone said "don't do this"? It seems to me C++ can do many things but has a horrible syntax even for the easiest things in many cases compared to C#.
一方面有人说 C++ 是最灵活的……但每个人都说“不要这样做”?在我看来,C++ 可以做很多事情,但与 C# 相比,即使是在许多情况下最简单的事情,它的语法也很糟糕。
Update 2
更新 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. I think bigger libraries / projects need a deeper nesting (I hate class names like ExtraLongClassNameBecauseEveryThingIsInTheSameNameSpace... then you could put everything into the global namespace, too.)
大多数用户表示创建比两个级别更深的嵌套是无稽之谈。好的,那么 Win8 开发中的 Windows::UI::Xaml 和 Windows::UI::Xaml::Controls::Primitives 命名空间呢?我认为微软对命名空间的使用是有道理的,它确实比 2 级更深。我认为更大的库/项目需要更深的嵌套(我讨厌像 ExtraLongClassNameBecauseEveryThingIsInTheSameNameSpace 这样的类名......然后你也可以把所有东西都放到全局命名空间中。)
Update 3 - Conclusion
更新 3 - 结论
Most say "don't do it", but... even boost has a deeper nesting then one or two levels. Yes, it is a library but: If you want reusable code - treat your own code like a library you would give someone else. I also use a deeper nesting for discovery purposes using namespaces.
大多数人说“不要这样做”,但是……即使是 boost 也有比一两个级别更深的嵌套。是的,它是一个库,但是:如果您想要可重用的代码 - 将您自己的代码视为您会给其他人的库。我还使用更深的嵌套来使用命名空间进行发现。
回答by W1M0R
C++17 might simplify nested namespace definition:
C++17 可能会简化嵌套命名空间定义:
namespace A::B::C {
}
is equivalent to
相当于
namespace A { namespace B { namespace C {
} } }
See (8) on namespace page on cppreference:
http://en.cppreference.com/w/cpp/language/namespace
在 cppreference 的命名空间页面上参见 (8):http:
//en.cppreference.com/w/cpp/language/namespace
回答by Kurt Hutchinson
To avoid really deep indenting, I usually do it this way:
为了避免真正的缩进,我通常这样做:
namespace A { namespace B { namespace C
{
class X
{
// ...
};
}}}
回答by Max Truxa
I fully support peterchen's answerbut want to add something that addresses another part of your question.
我完全支持peterchen 的回答,但想添加一些内容来解决您问题的另一部分。
Declaring namespaces is one of the very rare cases in C++ where I actually like the use of #define
s.
声明命名空间是 C++ 中非常罕见的情况之一,我实际上喜欢使用#define
s。
#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
This also removes the need for comments near the closing brace of the namespace (Did you ever scroll down to the bottom of a large source file and tried to add/remove/balance braces that were missing comments about which brace closes which scope? Not fun.).
这也消除了在命名空间的右大括号附近添加注释的需要(您是否曾经向下滚动到大型源文件的底部并尝试添加/删除/平衡缺少有关哪个大括号关闭哪个范围的注释的大括号?不好玩吗? .)
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;
For nesting deeper than three levels you would have to add helper macros up to the desired count.
为了嵌套超过三个级别,您必须添加辅助宏,直至达到所需的数量。
回答by Potatoswatter
C++ namespaces are used to group interfaces, not to divide components or express political division.
C++ 命名空间用于对接口进行分组,而不是划分组件或表达划分。
The standard goes out of its way to forbid Java-like use of namespaces. For example, namespace aliasesprovide a way to easily use deeply-nested or long namespace names.
该标准不遗余力地禁止类似 Java 的名称空间使用。例如,命名空间别名提供了一种轻松使用深度嵌套或长命名空间名称的方法。
namespace a {
namespace b {
namespace c {}
}
}
namespace nsc = a::b::c;
But namespace nsc {}
would then be an error, because a namespace may only be defined using its original-namespace-name. Essentially the standard makes things easy for the userof such a library but hard for the implementer. This discourages people from writing such things but mitigates the effects if they do.
但是namespace nsc {}
这将是一个错误,因为名称空间只能使用它的original-namespace-name来定义。本质上,该标准使此类库的用户易于操作,但对实现者来说却很困难。这会阻止人们写这样的东西,但如果他们这样做会减轻影响。
You should have one namespace per interface defined by a set of related classes and functions. Internal or optional sub-interfaces might go into nested namespaces. But more than two levels deep should be a very serious red flag.
每个接口应该有一个命名空间,由一组相关的类和函数定义。内部或可选的子接口可能会进入嵌套的命名空间。但是超过两层深度应该是一个非常严重的危险信号。
Consider using underscore characters and identifier prefixes where the ::
operator isn't needed.
考虑在::
不需要运算符的地方使用下划线字符和标识符前缀。
回答by peterchen
No, and please don't do that.
不,请不要那样做。
The purpose of namespaces is primarily resolving conflicts in the global namespace.
命名空间的目的主要是解决全局命名空间中的冲突。
A secondary purpose is local abbreviation of symbols; e.g. a complex UpdateUI
method may use an using namespace WndUI
to use shorter symbols.
次要目的是符号的本地缩写;例如,复杂的UpdateUI
方法可以使用 anusing namespace WndUI
来使用较短的符号。
I'm on a 1.3MLoc project, and the only namespaces we have are:
我在一个 1.3MLoc 项目中,我们拥有的唯一命名空间是:
- imported external COM libraries (mainly to isolate header conflicts between
#import
and#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
(anonymous namespaces in .cpp's, or
ModuleDetailHereBeTygers
namespaces in header-only libs) - enums are the biggest problem in my experience. They pollute like crazy.
- I still feel it's entirely too many namespaces
- 导入外部 COM 库(主要是为了隔离
#import
和之间的头冲突#include windows.h
) - 某些方面(UI、DB 访问等)的一级“公共 API”命名空间
- 不属于公共 API 的“实现细节”命名空间(.cpp 中的匿名命名空间,或
ModuleDetailHereBeTygers
仅标头库中的命名空间) - 枚举是我经验中最大的问题。他们像疯了一样污染。
- 我还是觉得命名空间太多了
In this project, class names etc. use a two- or three-letter "region" code (e.g. CDBNode
instead of DB::CNode
). If you prefer the latter, there's room for a second level of "public" namespaces, but no more.
在这个项目中,类名等使用两个或三个字母的“区域”代码(例如CDBNode
代替DB::CNode
)。如果您更喜欢后者,则还有第二级“公共”命名空间的空间,但仅此而已。
Class-specific enums etc. can be members of those classes (though I agree this is not always good, and it's sometimes hard to say whether you should)
特定于类的枚举等可以是这些类的成员(虽然我同意这并不总是好的,有时很难说你是否应该)
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 theminto a namespace is much easier to do.
也很少需要“公司”命名空间,除非您对以二进制形式分发的第 3 方库有大问题,不提供自己的命名空间,并且不能轻松地放入一个(例如,在二进制文件中)分配)。尽管如此,根据我的经验,强制它们进入命名空间要容易得多。
[edit]As per Stegi's follow-up question:
[编辑]根据 Stegi 的后续问题:
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
好的,那么 Win8 开发中的 Windows::UI::Xaml 和 Windows::UI::Xaml::Controls::Primitives 命名空间呢?我认为微软对命名空间的使用是有道理的,它确实比 2 级更深
Sorry if I wasn't clear enough: Two levels isn't an hard limit, and more isn't intrinsically bad. I just wanted to point out that you rarely needmore than two, from my experience, even on a large code base. Nesting deeper or more shallow is a tradeoff.
对不起,如果我不够清楚:两个级别不是硬限制,更多本质上并不是坏事。我只是想指出,根据我的经验,即使在大型代码库中,您也很少需要超过两个。嵌套更深或更浅是一种权衡。
Now, the Microsoft case is arguably different. Presumably a much larger team, and allthe code is library.
现在,微软的案例可以说是不同的。大概是一个更大的团队,所有的代码都是库。
I'd assume Microsoft is imitating here the success of the .NET Library, where namespaces contribute to the discoverabilityof the extensive library. (.NET has about 18000types.)
我认为微软正在模仿 .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.
我进一步假设命名空间中有一个最佳(数量级)符号。比如说,1 没有意义,100 听起来不错,10000 显然太多了。
TL;DR:It's a tradeoff, and we don't have hard numbers. Play safe, don't overdo in any direction. The "Don't do that" comes merely from the "You have problems with that, I'd have problems with that, and I don't see a reason why you'd need it.".
TL;DR:这是一个权衡,我们没有硬性数字。谨慎行事,不要向任何方向过度。“不要那样做”仅仅来自“你有问题,我有问题,我看不出你需要它的理由。”。
回答by CapelliC
Here a quote from Lzz(Lazy C++) docs:
这里引用了Lzz(Lazy C++) 文档:
Lzz recognizes the following C++ constructs:
namespace definition
An unnamed namespace and all enclosed declarations are output to the source file. This rule overrides all others.
The name of a named namespace may be qualified.
namespace A::B { typedef int I; }
is equivalent to:
namespace A { namespace B { typedef int I; } }
Lzz 识别以下 C++ 结构:
命名空间定义
未命名的命名空间和所有封闭的声明都输出到源文件。此规则覆盖所有其他规则。
命名空间的名称可以被限定。
namespace A::B { typedef int I; }
相当于:
namespace A { namespace B { typedef int I; } }
Of course the quality of sources that depends on such tools is debatable... I would say it's more a curiosity, showing that the syntax disease induced by C++ can take many form (I have mine, too...)
当然,依赖于这些工具的源的质量是有争议的......我会说这更像是一种好奇心,表明由 C++ 引起的语法疾病可以有多种形式(我也有我的......)
回答by Kirill Kobelev
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++2003 和 C++11)都非常明确地指出命名空间的名称是一个标识符。这意味着需要显式嵌套标头。
My impression that this is not a big deal to allow placing qualified identifier besides a simple name of the namespace, but for some reason this is not allowed.
我的印象是,除了命名空间的简单名称之外,允许放置限定标识符并不是什么大问题,但由于某种原因,这是不允许的。
回答by smac89
You can use this syntax:
您可以使用以下语法:
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 {
...
};
Note this syntax is valid even in C++98 and it is almost similar to what is now available in C++17 with nested namespace definitions.
请注意,即使在 C++98 中,此语法也是有效的,并且它几乎类似于现在在 C++17 中可用的嵌套命名空间定义。
Happy unnesting!
快乐解巢!
Sources:
资料来源:
回答by Questionable
This paper covers the subject rather well: Namespace Paper
这篇论文很好地涵盖了这个主题:命名空间论文
Which basically boils down this. The longer your namespaces are the more likely the chance that people are going to use the using namespace
directive.
这基本上归结为这一点。命名空间越长,人们使用该using namespace
指令的可能性就越大。
So looking at the following code you can see an example where this will hurt you:
所以看看下面的代码,你会看到一个例子,这会伤害你:
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{};
}
This code will compile fine, however, if you uncomment the line //using namespace def;
then the "testing" namespace will become ambiguous and you will have naming collisions. This means that your code base can go from stable to unstable by including a 3rd party library.
这段代码可以很好地编译,但是,如果取消注释该行,//using namespace def;
那么“testing”命名空间将变得不明确,并且会发生命名冲突。这意味着通过包含第 3 方库,您的代码库可以从稳定变为不稳定。
In C#, even if you were to use using abc;
and using def;
the compiler is able to recognize that testing::myClass
or even just myClass
is only in the abc::testing
namespace, but C++ will not recognize this and it is detected as a collision.
在 C# 中,即使您要使用using abc;
并且using def;
编译器能够识别testing::myClass
或什至myClass
仅在abc::testing
命名空间中,但 C++ 不会识别这一点,并将其检测为冲突。