双冒号 "::" 的意思是什么?

516
我在一个需要修改的类中找到了这行代码。
::Configuration * tmpCo = m_configurationDB;//pointer to current db

我不知道双冒号加在类名前面具体是什么意思。如果没有双冒号,我会理解为声明tmpCo是指向Configuration类对象的指针...但是前面的双冒号让我感到困惑。

我还发现:

typedef ::config::set ConfigSet;

14
不太觉得这是一个答案,所以我要评论一下:在这个上下文中,裸的 :: 表示从全局/匿名命名空间引用变量。 - wkl
9个回答

615

这将确保从全局命名空间开始解析,而不是从当前所在的命名空间开始。例如,如果您有两个不同的类都叫做Configuration

class Configuration; // class 1, in global namespace
namespace MyApp
{
    class Configuration; // class 2, different from class 1
    function blah()
    {
        // resolves to MyApp::Configuration, class 2
        Configuration::doStuff(...) 
        // resolves to top-level Configuration, class 1
        ::Configuration::doStuff(...)
    }
}

基本上,它允许您遍历到全局命名空间,因为您的名称可能会被另一个命名空间中的新定义覆盖,在这种情况下是 MyApp


::Configuration::doStuff(...) 中为什么要放置两组双冒号? - Azurespot
1
@WyattAnderson,不是第一组。我认为我明白两个术语之间的 :: 是指命名空间或类及其成员。但第一个是什么意思? - Azurespot
14
@Azurespot,这就是原帖提出的问题,这篇文章对此进行了解答,并确保使用来自全局命名空间的标识符。请再次查看示例。 - hungryWolf
1
@AbelTom 他们不必要有相同的名称,而且你应该给它们取不同的名字。这个功能只是为了防止在某种特殊情况下需要使用时存在。 - Ethan Maness
1
@AbelTom 他们不必要有相同的名称,而且你应该给它们取不同的名字。这个功能只是为了万一有一个特殊情况需要它存在。 - undefined
显示剩余2条评论

245

:: 运算符被称为作用域解析运算符,它的作用就是解析作用域。因此,通过在类型名称前面加上它,告诉编译器在全局命名空间中查找该类型。

示例:

int count = 0;

int main(void) {
  int count = 0;
  ::count = 1;  // set global count to 1
  count = 2;    // set local count to 2
  return 0;
}

3
我之前完全不知道这个,现在让我感觉很强大,谢谢! - Harsh

157
许多合理的答案已经给出。我想提供一个类比,可能对一些读者有帮助。当你在搜索要运行的程序时,:: 的工作方式很像文件系统目录分隔符'/'。考虑以下内容:
/path/to/executable

这非常明确——只有文件系统树中该精确位置的可执行文件才能匹配此规范,与当前生效的PATH无关。同样地...

::std::cout

...在C++命名空间"tree"中同样明确。

与这种绝对路径相反,你可以配置好的UNIX shell(例如zsh)来解析当前目录或者PATH环境变量中任何元素下的相对路径,所以如果PATH=/usr/bin:/usr/local/bin,并且你在/tmp,那么...

X11/xterm

如果找到了,它会愉快地运行/tmp/X11/xterm,否则会运行/usr/bin/X11/xterm,再否则会运行/usr/local/bin/X11/xterm。同样地,假设您在一个名为X的命名空间中,并且已经生效了"using namespace Y",那么...

std::cout

这些内容可以在任何一个::X::std::cout::std::cout::Y::std::cout或其他地方找到,这是由于参数依赖查找(ADL,又称Koenig查找)的缘故。因此,只有::std::cout确切地指明了你所指的对象,但幸运的是,没有理智的人会创建自己的类/结构或名为"std"或"cout"的任何东西,因此在实践中仅使用std::cout就可以了。

值得注意的区别

1)shell tend to use the first match using the ordering in PATH,而C ++在模糊时会给出编译器错误。

2)在C ++中,没有任何前导作用域的名称可以与当前命名空间匹配,而大多数UNIX shell只有在PATH中放置.时才这样做。

3)C ++始终搜索全局命名空间(就像在您的PATH中隐式地拥有/一样)。

关于命名空间和符号显式性的一般讨论

使用绝对路径::abc::def::...有时可以将您与正在使用的任何其他命名空间隔离开来,这些命名空间是您部分拥有但无法真正控制其内容的一部分,甚至包括您的库的客户端代码也使用的其他库。另一方面,它也将您更紧密地耦合到符号的现有“绝对”位置,并且会错过命名空间中隐式匹配的优点:减少耦合,更容易在命名空间之间移动代码,以及更简洁,可读性更强的源代码。

像许多事情一样,这是一个平衡的行为。 C ++标准将许多标识符放在std :: 下,这些标识符比cout 不太“独特”,程序员可能会在其代码中完全使用它们进行不同的操作(例如merge includes fill generate exchange queue toupper max )。 两个不相关的非标准库具有更高的使用相同标识符的机会,因为作者通常不知道或不太了解彼此。而且库 - 包括C ++标准库 - 随时间而改变其符号。所有这些都可能在重新编译旧代码时创建歧义,特别是当有大量使用using namespace 时:在此空间中最糟糕的事情是允许头文件中的using namespace 逃脱头文件范围,以便任意数量的直接和间接客户端代码无法自行决定要使用哪个命名空间以及如何管理模糊性。

因此,C++程序员的工具箱中一个领先的::是积极消除已知冲突和/或消除未来不确定性的一种工具。


15
好的比喻得到一个赞。在我看来,比喻作为一种教学工具被使用的还不够多。 - Trevor Boyd Smith
非常好的,非常清晰的答案。 - Luke Davis
另一个很好的类比是与域名有关的。example.org.明确地根植于全球命名空间,不像example.org那样模糊不清。但我猜有些读者对此可能不太熟悉。 - Toby Speight
另一个很好的类比是与域名有关的。example.org.明确地根植于全球命名空间,不像example.org那样含糊不清。但我猜有些读者对此不太熟悉。 - undefined

54

::是作用域解析运算符,用于指定某个东西的作用域。

例如,::单独表示全局作用域,不在任何其他命名空间内。

some::thing可以有以下的几种可能意义:

  • some是一个命名空间(在全局作用域或当前作用域之外)而thing则可以是类型函数对象嵌套命名空间
  • some是当前作用域中可用的,而thing则可以是这个类的成员对象函数类型
  • 在类成员函数中some可以是当前类型的一个基础类型(或是当前类型本身),而thing则可以是这个类的一个成员、类型函数对象

你还可以使用嵌套作用域,例如some::thing::bad。在这里,每个名称都可以是一个类型、对象或命名空间。除了最后一个名称bad还可以是一个函数之外,其它的都不可能是函数,因为函数不能公开其内部作用域中的任何东西。

回到你的例子,::thing只能表示全局作用域中的类型、函数、对象或命名空间。

你使用它的方式表明(在指针声明中),它是全局作用域中的一个类型。

希望这个答案完整且正确,有助于您理解作用域解析。


2
@obounaim 请看这段代码 http://liveworkspace.org/code/3Wabw0$5class some { protected: int thing; }; class some_ext : public some { float thing; void action(){ some::thing = 42; thing = 666; } };在这里,somesome_ext的基类。当你在some_ext的成员函数中写入some::thing时,它指的是基类型some中的thing对象。如果没有使用some::,那么thing就表示最近的作用域中的some_ext::thing。这样更清晰明了吗? - Klaim

23

:: 用于将某个东西(变量、函数、类、typedef等)链接到命名空间或类中。

如果在 :: 前面没有左手边,则说明您正在使用全局命名空间。

例如:

::doMyGlobalFunction();


13

它被称为作用域解析运算符,可以使用作用域解析运算符::引用隐藏的全局名称。
例如;

int x;
void f2()
{
   int x = 1; // hide global x
   ::x = 2; // assign to global x
   x = 2; // assign to local x
   // ...
}

12

(这个回答主要是为了方便谷歌搜索,因为问题提出者已经解决了他的问题。)在其他答案中已经解释了带有::的前缀的含义——作用域分辨符,但我想补充一下人们为什么会使用它。

它的含义是“从全局命名空间中获取名称,而不是其他任何地方”。但是为什么需要明确地拼写呢?

用例 - 命名空间冲突

当您在全局命名空间和本地/嵌套命名空间中具有相同的名称时,将使用本地的那一个。因此,如果您想要全局的那一个,请在名称前面加上::。这种情况已在@Wyatt Anderson的答案中描述,请参见他的示例。

用例 - 强调非成员函数

当你编写成员函数(方法)时,调用其他成员函数和调用非成员(自由)函数看起来很像:

class A {
   void DoSomething() {
      m_counter=0;
      ...
      Twist(data); 
      ...
      Bend(data);
      ...
      if(m_counter>0) exit(0);
   }
   int m_couner;
   ...
}

但是可能会出现Twist是类A的成员函数,而Bend是自由函数的情况。也就是说,Twist可以使用和修改m_couner,而Bend则不能。因此,如果你想确保m_counter保持为0,你需要检查Twist,但不需要检查Bend

因此,为了使这更加清晰明了,可以使用this->Twist来显示Twist是一个成员函数,或者使用::Bend来显示Bend是自由函数。或者两者兼备。当你进行或计划重构时,这非常有用。


7

::是定义命名空间的运算符。

例如,如果您想在代码中使用cout而不提及using namespace std;,则可以这样写:

std::cout << "test";

当没有提及命名空间时,就意味着该类属于全局命名空间。


0
"

“::”代表作用域解析运算符。如果两个不同的类中有相同名称的函数/方法,可以使用作用域解析运算符来访问特定类的方法。

"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接