[英]Regarding the global namespace in C++
在 C++ 中,我们应该在全局命名空间中添加::
吗?
例如,当使用 C 语言的 WinAPI 时,我应该使用::HANDLE
代替HANDLE
,使用::LoadLibrary
代替LoadLibrary
吗? C++ 对此有何评论? 考虑到可读性和可维护性等问题,这通常是个好主意吗?
C++ 中的名称可以是限定的和非限定的。 限定名称和非限定名称查找有不同的规则。 ::HANDLE
是限定名称,而HANDLE
是非限定名称。 考虑以下示例:
#include <windows.h>
int main()
{
int HANDLE;
HANDLE x; //ERROR HANDLE IS NOT A TYPE
::HANDLE y; //OK, qualified name lookup finds the global HANDLE
}
我认为选择HANDLE
与::HANDLE
是编码风格的问题。 当然,正如我的例子所暗示的那样,在某些情况下,排位赛是强制性的。 因此,您不妨使用::
以防万一,除非语法对您来说有些恶心。
由于 C 中不存在命名空间,因此不要使用 ::HANDLE 访问 HANDLE 类型。
使用前置 :: for global 命名空间是提高可读性的好主意,您知道要访问的类型来自全局命名空间。
此外,如果您在嵌套命名空间中并声明您自己的 HANDLE 类型(例如),那么编译器将使用这个而不是 windows.h 一个!
因此,在嵌套命名空间中工作时,总是更喜欢在名称之前使用 :: 。
主要的兴趣点是从编译器的角度来看有什么区别,正如已经说过的那样,如果您包含::
那么您使用的是合格的查找,而不是不合格的查找。
使用限定查找的优势在于它能够始终精确定位特定符号。 缺点是它总是会查明特定符号——即它会禁用参数相关查找。 ADL 是该语言的一个重要且有用的部分,通过限定您可以有效地禁用它,这很糟糕。
假设您在全局命名空间中有一个函数f
,并且您在命名空间N
添加了一个类型T
。 不考虑您想添加f
的重载,该重载将T
作为参数。 遵循接口原则,可以将f
添加到N
命名空间中,因为f
实际上是对T
执行的操作,因此它属于类型。 在这种情况下,如果您的代码在未知类型U
的对象上调用(考虑泛型代码) ::f(obj)
,编译器将无法将::N::f(obj)
作为潜在的重载因为代码明确要求全局命名空间中的重载。
使用非限定查找使您可以自由地定义函数所属的位置以及用作参数的类型。 虽然它不完全相同,但请考虑使用swap
,如果您符合std::swap
资格,那么它不会在您的N
命名空间内拿起您的手卷void swap( T&, T& )
...
我只会在编译器不会选择我想要的元素时完全限定标识符。
这主要是风格问题; 没有性能或效率问题可言。 对于打算在许多不同平台上编译的大型项目和项目,这可能是一个很好的做法,因为在这些情况下,全局名称和命名空间中的名称之间的冲突更有可能发生。
通常情况下,你不必在前面加上::
全球命名空间。 (仅在某些非常罕见的情况下)。 恕我直言,它会损害可读性,但是,另一方面,它可能不会破坏您的代码
我将所有代码都放入一个命名空间中,并且我倾向于使用 C++ 标头而不是 C 标头,因此全局命名空间中剩下的唯一符号往往来自 Windows API。 我避免将符号从其他名称空间拉入当前名称空间(例如,我从未using namespace std;
过using namespace std;
),而是更喜欢明确限定事物。 这符合Google 的 C++ 风格指南。
因此,出于以下几个原因,我养成了使用::
限定 WinAPI 函数调用的习惯:
一致性。 对于当前命名空间之外的所有内容,我都明确引用它(例如std::string
),那么为什么不明确引用 Windows API(例如::LoadLibraryW
)? Windows APIs 命名空间是全局命名空间。
许多 WinAPI 函数都是通用命名的(例如, DeleteObject
)。 除非您非常熟悉正在阅读的代码,否则您可能不知道DeleteObject
是对当前命名空间中的某些内容的调用还是对 Windows API 的调用。 因此,我发现::
澄清了。
许多 Windows 框架都有与原始调用同名的方法。 例如, ATL::CWindow
有一个GetClientRect
方法,其签名与 WinAPI 的GetClientRect
略有不同。 在这个框架中,你的类通常是从ATL::CWindow
派生的,所以,在你的类的实现中,如果你需要调用 WinAPI 函数,通常说GetClientRect
调用继承的 ATL 方法和::GetClientRect
。 这不是绝对必要的,因为编译器会根据签名找到正确的。 尽管如此,我发现这种区别为读者澄清了。
(我知道这个问题并不是真的关于 WinAPI,但这个例子是关于 WinAPI 的。)
不,如果您的类中没有LoadLibrary
方法,则不需要使用全局范围。 事实上,你不应该使用全局作用域,因为如果你稍后将LoadLibrary
添加到你的类中,你的意图可能是覆盖全局函数......
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.