前置双冒号“ ::”是什么意思?


410

我在必须修改的类中找到了以下代码行:

::Configuration * tmpCo = m_configurationDB;//pointer to current db

而且我不知道在类名前面加双冒号到底是什么意思。否则,我会读到:tmpCo作为类的对象的指针的声明Configuration...但是前置的双冒号使我感到困惑。

我还发现:

typedef ::config::set ConfigSet;

7
真的不觉得这是一个答案,所以我要评论:en.wikipedia.org/wiki/Scope_resolution_operator。在这种情况下,裸::手段指的是来自全局/匿名命名空间的变量。
wkl

Answers:


490

这样可以确保解析是从全局名称空间进行的,而不是从当前所在的名称空间开始。例如,如果您有两个这样的不同类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's

@NoniA。您是在问第二套双冒号吗?
FCo

1
@WyattAnderson,没有第一组。我想我知道,::两个术语之间是指名称空间或类及其成员。但是第一个呢?
Azurespot

6
OP会问@Azurespot,这是此帖子回答的问题。它确保使用全局名称空间中的标识符。再看一个例子
hungryWolf

193

::运算符称为作用域解析运算符,正是这样,它才能解析范围。因此,通过在类型名称前添加前缀,它告诉编译器在全局名称空间中查找该类型。

例:

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;
}

122

已经有很多合理的答案。我会打个比方,以帮助一些读者。 在路径中搜索要运行的程序时,::其工作原理与文件系统目录分隔符“ /”非常相似。考虑:

/path/to/executable

这非常明确-不管有效的PATH如何,只有文件系统树中该确切位置的可执行文件都可以匹配此规范。同样...

::std::cout

...在C ++名称空间“树”中同样明确。

与此类绝对路径相反,您可以配置良好的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倾向于使用中的顺序使用第一个匹配项PATH,而当您不清楚时,C ++会给出编译器错误。

2)在C ++中,可以在当前名称空间中匹配没有任何前导作用域的名称,而大多数UNIX shell仅.在将PATH。放在其中时才能进行匹配。

3)C ++始终搜索全局名称空间(例如/隐式地包含your PATH)。

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

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

与许多事情一样,这是一种平衡行为。C ++标准放大量下标识符std::是小于“独一无二”的cout,程序员可以使用在他们的代码完全不同的东西(例如mergeincludesfillgenerateexchangequeuetouppermax)。两个不相关的非标准库使用相同标识符的可能性要高得多,因为作者通常彼此之间不太了解。库-包括C ++标准库-会随着时间的推移更改其符号。当重新编译旧代码时,所有这些可能会造成歧义,尤其是在大量使用using namespace s:在此空间中最糟糕的事情是允许using namespace标头中的s可以逃避标头的范围,以至于任意数量的直接和间接客户端代码无法自行决定使用哪个名称空间以及如何管理歧义。

因此,领导::是C ++程序员工具箱中的一种工具,用于主动消除已知冲突,和/或消除将来出现歧义的可能性...。


8
+1是一个很好的类比。几乎没有足够的类比将IMO用作教学工具。
特雷弗·博伊德·史密斯

38

::是范围解析运算符。它用于指定事物的范围。

例如,::在所有其他名称空间之外,仅是全局范围。

some::thing 可以通过以下任何一种方式进行解释:

  • some是一个名称空间(在全局范围内,或在当前范围之外的外部范围内),并且thing类型函数对象嵌套名称空间
  • some是一个在当前范围内可用的并且thing是一个成员对象功能类型的的some类;
  • 在类成员函数中some可以是当前类型的基本类型(或当前类型本身),thing然后是该类的一个成员,类型函数对象

您还可以嵌套嵌套范围,如中所示some::thing::bad。在这里,每个名称可以是类型,对象或名称空间。此外,最后一个bad也可以是一个函数。其他函数则不能,因为函数无法在其内部范围内公开任何内容。

因此,回到您的示例,::thing它只能是全局范围内的某种东西:类型,函数,对象或名称空间。

您使用它的方式表明(用在指针声明中)它是全局范围中的一种类型。

我希望这个答案是完整且正确的,以帮助您了解范围解析。


2
@obounaim考虑以下代码liveworkspace.org/code/3Wabw0$5 class some { protected: int thing; }; class some_ext : public some { float thing; void action(){ some::thing = 42; thing = 666; } };some是的基类,some_ext当您写入some::thingsome_ext的成员函数时,意味着该thing对象成为基类some。没有some::thing单独表示thing最接近的范围,即some_ext::thing。更清楚了吗?
Klaim

17

:: 用于将某些内容(变量,函数,类,typedef等)链接到名称空间或类。

如果之前没有左手边::,则表明您正在使用全局名称空间。

例如:

::doMyGlobalFunction();


10

它的调用范围解析运算符,可以使用范围解析运算符::来引用一个隐藏的全局名称

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

10

(此答案主要是针对Google员工的,因为OP已经解决了他的问题。)::在其他答案中已经说明了前置的含义-范围重新确定运算符-,但我想补充一下人们为什么使用它。

含义是“从全局名称空间获取名称,而不是其他任何名称”。但是,为什么需要对此进行明确的拼写?

用例-名称空间冲突

当您在全局名称空间和本地/嵌套名称空间中具有相同的名称时,将使用本地名称。因此,如果您想要全局的,请在前面加上::。@Wyatt Anderson的回答描述了这个案例,请看他的例子。

用例-强调非成员功能

在编写成员函数(方法)时,对其他成员函数的调用和对非成员(自由)函数的调用看起来相似:

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

但是可能Twist是class的姐妹成员函数A,并且Bend是自由函数。也就是说,Twist可以使用和修改m_counerBend不能。因此,如果要确保m_counter保持为0,则必须检查Twist,但是不需要进行检查Bend

因此,为了使这更清楚地突出,一个可以写this->Twist向读者展示这Twist是一个成员函数或写::Bend证明Bend是免费的。或两者。在进行或计划重构时,这非常有用。


5

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

例如,如果要使用cout而不using namespace std;在代码中提及,请编写以下代码:

std::cout << "test";

当没有提到命名空间时,可以说该类属于全局命名空间。


1

“ ::”代表范围解析运算符。可以在两个不同的类中定义具有相同名称的函数/方法。要访问特定类范围的方法,请使用解析运算符。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.