Зачем C ++ нужен оператор разрешения области видимости? - PullRequest
36 голосов
/ 18 февраля 2012

(я знаю, что делает оператор разрешения области видимости, и как и когда его использовать.)

Почему в C ++ есть оператор :: вместо использования оператора . для этой цели? У Java нет отдельного оператора, и он отлично работает. Есть ли какая-то разница между C ++ и Java, что означает, что C ++ требует отдельного оператора для синтаксического анализа?

Мое единственное предположение, что :: необходим по причинам старшинства, но я не могу понять, почему он должен иметь более высокий приоритет, чем, скажем, .. Единственная ситуация, которую я могу себе представить, это что-то вроде

a.b::c;

будет проанализирован как

a.(b::c);

, но я не могу вспомнить ни одной ситуации, в которой такой синтаксис был бы в любом случае законным.

Может быть, это просто случай "они делают разные вещи, поэтому они могли бы также выглядеть по-другому". Но это не объясняет, почему :: имеет более высокий приоритет, чем ..

Ответы [ 7 ]

29 голосов
/ 18 февраля 2012

Потому что кто-то из комитета по стандартам 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;
}

По сути, это позволяет переменной-члену и производному типу класса иметь одно и то же имя. Я понятия не имею , что кто-то курил, когда они думали, что это важно. Но это так.

Когда компилятор видит ., он знает, что объект слева должен быть объектом. Когда он видит ::, это должно быть имя типа или пространство имен (или ничего, указывающее глобальное пространство имен). Вот как это решает эту двусмысленность.

28 голосов
/ 18 февраля 2012

Почему 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()’

Inчтобы позвонить b.f() (точнее A::f()), вам нужен оператор разрешения области действия:

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

Демо на ideone

9 голосов
/ 18 февраля 2012

В отличие от 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;
}
6 голосов
/ 09 апреля 2016

Почему в C ++ есть оператор :: вместо использования.оператор для этой цели?

Причина указана самим Страуструпом:

В C с классами точка использовалась для выражения членства в классе, а также для выражениявыбор члена конкретного объекта.

Это послужило причиной некоторой небольшой путаницы и могло также использоваться для построения неоднозначных примеров.Чтобы облегчить это, :: был введен для обозначения принадлежности к классу, а . был сохранен исключительно для членства объекта

(Бьярне Страуструп История C ++: 1979-1991 стр. 21 - § 3.3.1)

Более того, верно, что

они делают разные вещи, поэтому они могут также выглядеть по-разному

действительно

В N::m ни N, ни m не являются выражениями со значениями;N и m являются именами, известными компилятору, а :: выполняет разрешение области действия (времени компиляции), а не оценку выражения.Можно представить, что можно разрешить перегрузку x :: y, где x является объектом, а не пространством имен или классом, но это, вопреки первому появлению, потребует введения нового синтаксиса (чтобы разрешить expr::expr).Неясно, какие преимущества принесло бы такое осложнение.

Оператор . (точка) в принципе может быть перегружен с использованием той же техники, что и для ->.

(FAQ по стилю и технике Бьярна Страуструпа C ++ )

3 голосов
/ 18 февраля 2012

Просто чтобы ответить на последний вопрос о приоритете оператора:

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;
}
1 голос
/ 02 марта 2015

Оператор разрешения области (: :) используется для определения функции вне класса или когда мы хотим использовать глобальную переменную, но также имеет локальную переменную с тем же именем.

1 голос
/ 29 декабря 2013

Я всегда предполагал, что использование C ++ dot / :: - это выбор стиля, чтобы сделать код проще для чтения.Поскольку ОП пишет: «Они делают разные вещи, поэтому должны выглядеть по-разному».

Давным-давно из C ++ в C # я обнаружил, что использование только точек сбивает с толку.Я привык видеть A::doStuff(); B.doStuff();, и зная, что первая - обычная функция в пространстве имен, а вторая - функция-член в экземпляре B.

C ++, возможно, мой пятый язык послеBasic, ассемблер, Pascal и Fortran, так что я не думаю, что это синдром первого языка, и теперь я больше программист на C #.Но, IMHO, если вы использовали оба, двойная двоеточие в стиле C ++ для пространств имен читается лучше.Я чувствую, что Java / C # выбрал точки для обоих (успешно) облегчить переднюю часть кривой обучения.

...