为什么C ++需要范围解析运算符?

时间:2012-02-18 03:20:38

标签: c++ operators language-design scope-resolution

(我知道范围解析操作符的作用,以及如何以及何时使用它。)

为什么C ++有::运算符,而不是为此目的使用.运算符? Java没有单独的运算符,并且工作正常。 C ++和Java之间是否有一些区别,这意味着C ++需要一个单独的运算符才能被解析?

我唯一的猜测是出于优先原因需要::,但我想不出为什么它需要比.具有更高的优先级。我能想到的唯一情况就是像

那样
a.b::c;

将被解析为

a.(b::c);

,但我无法想到这样的语法无论如何都是合法的。

也许这只是“他们做不同的事情,所以他们可能看起来不同”的情况。但这并不能解释为什么::的优先级高于.

7 个答案:

答案 0 :(得分:29)

因为C ++标准委员会中有人认为允许此代码工作是个好主意:

struct foo
{
  int blah;
};

struct thingy
{
  int data;
};

struct bar : public foo
{
  thingy foo;
};

int main()
{
  bar test;
  test.foo.data = 5;
  test.foo::blah = 10;
  return 0;
}

基本上,它允许成员变量和派生类类型具有相同的名称。当他们认为这很重要时,我不知道有人在吸烟。但它就是。

当编译器看到.时,它知道左边的东西必须是一个对象。当它看到::时,它必须是一个typename或命名空间(或者什么都没有,表示全局命名空间)。这就是解决这种歧义的方法。

答案 1 :(得分:28)

为什么C ++在使用.的地方不使用::,因为这就是语言的定义方式。一个可能的原因可能是,使用语法::a引用全局命名空间,如下所示:

int a = 10;
namespace M
{
    int a = 20;
    namespace N
    {
           int a = 30;
           void f()
           {
              int x = a; //a refers to the name inside N, same as M::N::a
              int y = M::a; //M::a refers to the name inside M
              int z = ::a; //::a refers to the name in the global namespace

              std::cout<< x <<","<< y <<","<< z <<std::endl; //30,20,10
           }
    }
}

Online Demo

我不知道Java如何解决这个问题。我甚至不知道在Java中是否存在全局命名空间。在C#中,您使用语法global::a来引用全局名称,这意味着即使C#也有::运算符。


  

但是我无法想到这样的语法无论如何都是合法的。

谁说像a.b::c这样的语法不合法?

考虑这些课程:

struct A
{
    void f() { std::cout << "A::f()" << std::endl; }
};

struct B : A
{
    void f(int) { std::cout << "B::f(int)" << std::endl; }
};

现在看到这个(ideone):

B b;
b.f(10); //ok
b.f();   //error - as the function is hidden
无法像这样调用

b.f(),因为该函数被隐藏,并且GCC提供了此错误消息:

error: no matching function for call to ‘B::f()’

要调用b.f()(或更确切地说A::f()),您需要范围解析运算符:

b.A::f(); //ok - explicitly selecting the hidden function using scope resolution

Demo at ideone

答案 2 :(得分:9)

与Java不同,C ++具有多重继承。下面是一个例子,你所讨论的那种范围分辨率变得很重要:

#include <iostream>
using namespace std;
struct a
{
    int x;
};
struct b
{
    int x;
};
struct c : public a, public b
{
    ::a a;
    ::b b;
};
int main() {
    c v;
    v.a::x = 5;
    v.a.x = 55;
    v.b::x = 6;
    v.b.x = 66;
    cout << v.a::x << " " << v.b::x << endl;
    cout << v.a.x << " " << v.b.x << endl;
    return 0;
}

答案 3 :(得分:6)

  

为什么C ++有::运算符,而不是使用。为此目的的运营商?

Stroustrup本人给出了原因:

  

在C with Classes中,使用点来表示类的成员资格以及表达对特定对象成员的选择。

     

这是造成一些轻微混淆的原因,也可用于构建含糊不清的例子。为了缓解这种情况,::被引入以表示类的成员资格,而.仅为对象成员资格保留

(Bjarne Stroustrup A History of C++: 1979−1991第21页 - §3.3.1)

此外,确实

  

他们做不同的事情,所以他们可能看起来不同

确实

  

N::m中,Nm都不是包含值的表达式; Nm是编译器已知的名称,::执行(编译时)范围解析而不是表达式求值。可以想象允许重载x :: y,其中x是对象而不是命名空间或类,但这与首次出现相反 - 涉及引入新语法(允许expr::expr)。这种并发症带来的好处并不明显。

     

运算符.(点)原则上可以使用与->相同的技术重载。

(Bjarne Stroustrup的C++ Style and Technique FAQ

答案 4 :(得分:3)

回答关于运算符优先级的问题的最后一点:

class A {
public:
  char A;
};

class B : public A {
public:
  double A;
};

int main(int c, char** v)
{
  B myB;
  myB.A = 7.89;
  myB.A::A = 'a';
  // On the line above a hypothetical myB.A.A
  // syntax would parse as (myB.A).A and since
  // (myB.A) is of type double you get (double).A in the
  // next step. Of course the '.' operator has no
  // meaning for doubles so it causes a syntax error. 
  // For this reason a different operator that binds
  // more strongly than '.' is needed.
  return 0;
}

答案 5 :(得分:1)

我总是假设C ++ dot / :: usage是一种样式选择,使代码更容易阅读。由于OP写道“他们做了不同的事情,所以应该看起来不同。”

很久以前,从C ++来到C#,我发现只使用了令人困惑的点。我习惯于看到A::doStuff(); B.doStuff();,并且知道第一个是常规函数,在命名空间中,第二个是实例B上的成员函数。

C ++可能是我的第五语言,在Basic,汇编,Pascal和Fortran之后,所以我认为它不是第一语言综合症,现在我更像是一个C#程序员。但是,恕我直言,如果你已经使用过两者,那么C ++风格的双冒号用于命名空间读取效果会更好。我觉得Java / C#选择了两个点(成功)缓解了学习曲线的前沿。

答案 6 :(得分:1)

范围解析运算符(::)用于定义类外部的函数,或者当我们想要使用全局变量但还有一个具有相同名称的局部变量时。