簡體   English   中英

為什么 C++ 需要范圍解析運算符?

[英]Why does C++ need the scope resolution operator?

(我知道范圍解析運算符的作用,以及如何以及何時使用它。)

為什么 C++ 有::運算符,而不是使用. 運營商為此目的? Java 沒有單獨的運算符,並且工作正常。 C++ 和 Java 之間是否存在一些差異,這意味着 C++ 需要單獨的運算符才能進行解析?

我唯一的猜測是::出於優先級原因需要,但我想不出它為什么需要比 . 等更高的優先級. . 我能想到的唯一情況是這樣的

a.b::c;

將被解析為

a.(b::c);

,但我想不出這樣的語法在任何情況下都是合法的。

也許這只是“他們做不同的事情,所以他們可能看起來不同”的情況。 但這並不能解釋為什么::的優先級高於. .

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

基本上,它允許成員變量和派生類類型具有相同的名稱。 當他們認為這很重要時,我不知道有人在吸煙。 但它就在那里。

當編譯器看到. ,它知道左邊的東西一定是一個對象。 當它看到::時,它必須是類型名或命名空間(或者什么都不是,表示全局命名空間)。 這就是它解決這種歧義的方法。

為什么 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# 也有::運算符。


但我想不出這樣的語法在任何情況下都是合法的。

誰說像ab::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

bf()不能這樣調用,因為該函數是隱藏的,並且 GCC 會給出以下錯誤消息:

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

為了調用bf() (或者更確切地說A::f() ),您需要范圍解析運算符:

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

在 ideone 演示

為什么 C++ 有 :: 運算符,而不是使用 . 運營商為此目的?

原因由 Stroustrup 本人給出:

在帶有類的 C 中,點用於表示類的成員資格以及表示對特定對象成員的選擇。

這是造成一些輕微混淆的原因,也可以用來構建模棱兩可的例子。 為了緩解這種情況,引入了::來表示類的成員資格和. 專門為對象的成員資格而保留

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

此外,這是真的

他們做不同的事情,所以他們可能看起來不同

的確

N::m中, Nm都不是帶值的表達式; Nm是編譯器已知的名稱, ::執行(編譯時)范圍解析而不是表達式評估。 可以想象允許 x::y 重載,其中 x 是一個對象而不是名稱空間或類,但這將 - 與第一次出現相反 - 涉及引入新語法(以允許expr::expr )。 目前尚不清楚這種並發症會帶來什么好處。

運算符. (dot) 原則上可以使用與->相同的技術進行重載。

(Bjarne Stroustrup 的C++ 風格和技術常見問題解答

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

我一直認為 C++ dot/:: 用法是一種風格選擇,以使代碼更易於閱讀。 正如 OP 所寫的那樣“他們做不同的事情,所以看起來應該不同。”

從很久以前的 C++ 到 C#,我發現只使用點令人困惑。 我習慣於看到A::doStuff(); B.doStuff(); ,並且知道第一個是命名空間中的常規函數​​,第二個是實例 B 上的成員函數。

C++ 可能是我在 Basic、匯編、Pascal 和 Fortran 之后的第五種語言,所以我不認為它是第一語言綜合症,我現在更像是一名 C# 程序員。 但是,恕我直言,如果您同時使用了兩者,那么命名空間的 C++ 樣式雙冒號會更好讀。 我覺得 Java/C# 為兩者都選擇了點來(成功地)緩解學習曲線的前端。

只是為了回答有關運算符優先級的問題的最后一點:

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

作用域解析運算符(::) 用於定義類外部的函數,或者當我們想要使用全局變量但也有同名的局部變量時。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM