何时在C++中使用"::"进行全局范围?

When to use "::" for global scope in C++?

本文关键字:全局 范围 何时 C++      更新时间:2023-10-16

每隔一段时间,我就会偶然发现一些我正在维护的代码,这些代码挑战了我对代码风格的思考方式。今天就是这样的一天。。。

我知道为什么要使用scope运算符来定义全局作用域。事实上,这里没有作用域的作用域解析操作符是一个很好的链接,告诉你为什么。

然而,我今天看到了一些让我思考的东西。所有有问题的类都被封装到项目的命名空间中(很好!),但我确实看到了全局作用域运算符的大量使用。也就是说,它被用于C库的所有内容(除了uint8_t等…是的,程序员使用了这个库的.h版本,因为显然他们运行的g++版本仍然发出了关于新C++标准的警告)。这有用吗?我认为这只是浪费字符(提醒我使用this指针……除了在复制构造函数和赋值运算符的情况下,它有助于澄清哪个对象是哪个对象)。我是不是错过了什么?当然,有些人可以按照usleep()或stderr(我看到"::"用法最多的地方)来命名一些东西,但他们不知道这样做可能会破坏一些可怕的东西吗?在什么时候,你会用作用域运算符来表示"去死吧",并告诉自己,在你的命名空间中以某种方式命名函数的人是在找麻烦?

所以我的问题是…在这种情况下,使用全局作用域运算符的"正确"(我理解为主观)方式是什么?std或您自己的命名空间中没有包含的所有内容都应该明确定义全局作用域吗?我倾向于谨慎行事,并使用"std::"来避免使用指令,但在这里使用全局作用域运算符有什么好处吗?为了清晰起见,我倾向于认为这确实有助于我们没有从当前命名空间中获得有问题的变量或函数,但我在包括它和不考虑今天的发展之间左右为难。

和往常一样,感谢您的帮助和指导,因为我希望让我的代码更干净、更可读,而且(当然)明显更棒。

我很少使用它;只有当出于任何原因需要解决某些歧义时。不过,这是相当主观的。

在某些情况下(比如说,在模板中),你可能担心ADL只在某些情况中会导致歧义:

template <typename T>
void foo(T t)
{
   ::bar(t);  // :: just in case there's a `bar` in `T`'s namespace
}

这个问题几乎没有正确的答案,因为它几乎完全与样式相关,只是如果你认为你可能想改变导入一些声明/定义的位置,在这种情况下,当你使用它们时,您不指定任何范围,而是使用using指令(从命名空间导入子集)或using namespace指令从命名空间导入整个集合来导入它们。

大多数情况下,using指令被用作方便指令,但它是指导使用哪些声明/定义的强大方法。我倾向于不指定范围并导入声明。这样做可以在需要时进行简单的更改,同时减少视觉噪音。此外,指定范围意味着我将被"锁定"在获取声明的位置(好吧,我必须进行全局搜索和替换才能更改它)。

如果发生冲突(您尝试使用从多个命名空间导入的具有相同名称的已声明项),编译器会通知您,因此没有真正的危险。

可读代码的噪声最小。命名空间前缀通常只提供噪声。所以基线是根本没有它们。

命名空间被引入C++主要是为了处理第三方无法控制的东西。为了允许库删除前缀,而客户端可以通过使用应用来使用简洁的名称。

仅仅因为可以在许多命名空间中具有相同的名称,并不意味着这也是一个好主意。如果一个项目使用某些环境、平台API、库集,无论什么将名称放在全局中,为了其他目的,最好避免使用这些名称。无论有没有前缀,它们都会承担心理开销。

在形状良好的代码中很少使用:,并且在包装器类中经常使用相同的功能。

考虑以下情况。

公共图书馆

您正在编写一个具有公共标头的可导出库。而且您完全不知道在什么环境中会包含您的标头。例如,有人可能会这样做:

namespace std = my_space::std;
#include "your_header"

如果你简单地使用:std::list<int>等,那么你的所有定义都会被破坏。所以,在全局的所有内容之前加上::是一个很好的做法。这样你就可以绝对确定你在用什么。当然,您可以执行using(在C++11中)或typedef,但在标头中这样做是错误的。


协作.cc/.cpp文件

在你自己的代码中,这些代码不会以任何方式公开,但仍然可以由你自己编辑——这是一个很好的做法,可以从命名空间之外宣布你将要使用的。比方说,您的项目允许使用多个向量,而不仅仅是std::vector。然后,在适当的地方,放置using指令:

// some includes
using vector = ::std::vector<int>;  // C++11
typedef ::std::vector<int> vector;  // C++03
namespace my_namespace {
...
}  // namespace my_namespace

它可以放在所有包含的函数中,也可以放在特定函数中。它不仅提供了对代码的控制,还使代码可读并缩短了表达式。

另一种常见的情况是全局C函数和C++代码的混合。使用函数名执行任何typedef都不是一个好主意。在这种情况下,当有人在同一名称空间中实现具有相同签名的函数时,您应该在C函数之前使用全局范围解析运算符::来避免编译问题。

不要将::用于相对类型和名称空间,否则您将完全失去使用名称空间的好处。


您自己的代码

做你想做的事,怎么做。只有一种好的方法——用自己的代码轻松填充。真的,在这种情况下,如果不需要解决歧义,就不用担心全局范围的解决。