40

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

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

我唯一的猜测是::出于优先级原因需要,但我想不出为什么它需要比.. 我能想到的唯一情况是这样的

a.b::c;

将被解析为

a.(b::c);

,但我想不出这样的语法在任何情况下都是合法的。

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

4

7 回答 7

34

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

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

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

于 2012-02-18T03:52:32.350 回答
30

为什么 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
           }
    }
}

在线演示

我不知道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

在 ideone 演示

于 2012-02-18T03:38:32.820 回答
10

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

原因由 Stroustrup 本人给出:

在带有类的 C 中,点用于表示类的成员资格以及表示对特定对象成员的选择。

这是造成一些轻微混淆的原因,也可以用来构建模棱两可的例子。为了缓解这种情况,::引入了表示类成员资格并.专门保留为对象成员资格

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

此外,这是真的

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

确实

N::m既不是N也不是有m值的表达式;N并且m是编译器已知的名称,并::执行(编译时)范围解析而不是表达式评估。可以想象允许 x::y 重载,其中 x 是一个对象而不是名称空间或类,但这将 - 与第一次出现相反 - 涉及引入新语法(允许expr::expr)。目前尚不清楚这种并发症会带来什么好处。

操作符.(点)原则上可以使用与 . 相同的技术进行重载->

(Bjarne Stroustrup 的C++ 风格和技术常见问题解答

于 2016-04-09T15:40:16.620 回答
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;
}
于 2012-02-18T03:39:22.823 回答
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;
}
于 2012-02-18T04:20:28.903 回答
3

我一直认为 C++ dot/:: 用法是一种风格选择,以使代码更易于阅读。正如 OP 所写的那样“他们做不同的事情,所以看起来应该不同。”

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

C++ 可能是我在 Basic、汇编、Pascal 和 Fortran 之后的第五种语言,所以我不认为它是第一语言综合症,我现在更像是一名 C# 程序员。但是,恕我直言,如果您同时使用了两者,那么命名空间的 C++ 样式双冒号会更好读。我觉得 Java/C# 为两者都选择了点来(成功地)缓解学习曲线的前端。

于 2013-12-29T19:05:26.430 回答
1

作用域解析运算符(::) 用于定义类外部的函数,或者当我们想要使用全局变量但也有同名的局部变量时。

于 2015-03-02T17:04:29.587 回答