вопрос о приоритете адресов операторов С++ и разрешения области видимости

Здравствуйте, у меня есть этот код с ошибкой компилятора (ошибка из Microsoft Visual Studio 2008):

class B {
protected:
    int b;
};

class A : public B {
public:
    void foo() { &B::b; } 
// error C2248: 'B::b' : cannot access protected member declared in class 'B'
};

пока этот код не содержит ошибок:

class B {
protected:
    int b;
};

class A : public B {
public:
    void foo() { &(B::b); }
};

Два фрагмента кажутся мне эквивалентными, основываясь на моем знании приоритета операторов, потому что :: имеет более высокий приоритет, чем & (см., например, таблицу 2 на странице 137 JOINT STRIKE FIGHTER AIR Vehicle C++ СТАНДАРТЫ КОДИРОВКИ ДЛЯ ПРОГРАММЫ РАЗРАБОТКИ СИСТЕМЫ И ДЕМОНСТРАЦИИ )

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

Любое объяснение?


person Alessandro Jacopson    schedule 16.02.2011    source источник
comment
Если бы приоритет был неправильным, то это все равно была бы (другая) синтаксическая ошибка?   -  person Flexo    schedule 16.02.2011
comment
Обратите внимание на разницу: int* i = &(A::b);, но int A::*m = &A::b;   -  person Gene Bushuyev    schedule 16.02.2011


Ответы (3)


В первом случае вы берете адрес указателя на элемент B::b. Поскольку такой указатель НЕ является членом родителя A, а является отдельным объектом, он не может получить к нему доступ через защищенный механизм.

Во ВТОРОМ случае, когда это работает, вы запрашиваете адрес конкретного экземпляра b, уточняя его базовым классом, чтобы в случае множественного наследования компилятор знал, какой базовый класс вы используете. иметь в виду. В этом контексте защищенный атрибут виден.

Обратите внимание, что это компилируется:

class B
{
protected:
int b;
};

class A : public B
{
public:
void foo(){ &A::b; }  // Note here &A:: instead of &B::
};

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

class B
{
protected:
int b;
};

class A : public B
{
public:
void foo(const B* b_obj) { b_obj->b; }
};
person Mark B    schedule 16.02.2011
comment
он также скомпилирует &this->B::b; - person Gene Bushuyev; 16.02.2011
comment
@Gene Bushuyev Правильно, это точно эквивалентно второму (рабочему) примеру ОП: получить адрес конкретного члена b родителя, а не общий указатель на член. - person Mark B; 16.02.2011
comment
Любая ссылка из стандарта, в которой говорится о синтаксисе и различии? Как этот синтаксис &(B::b) означает, что b принадлежит конкретному экземпляру A? - person Nawaz; 16.02.2011
comment
@Nawaz: B::b - это типичный синтаксис устранения неоднозначности класса, который можно использовать для ссылки либо на элементы данных, либо на методы. Удивляет только взаимодействие со скобками и &. - person Matthieu M.; 16.02.2011
comment
@Матье: Да. Я понимаю, что означает B::b. На самом деле я хотел спросить, что такого особенного в скобках, что имеет значение? - person Nawaz; 16.02.2011
comment
@ Наваз, посмотри ответ Исэ, кажется, он очень хорошо это объясняет. Оказывается, это не вопрос приоритета, а особенность грамматики. - person Sergei Tachenov; 16.02.2011
comment
@Сергей: я видел. Спасибо. :-) - person Nawaz; 16.02.2011

Это всего лишь дополнение.
В §5.3.1/2 говорится:

Результатом унарного оператора & является указатель на его операнд. Операнд должен быть lvalue или квалифицированным идентификатором. В первом случае, если тип выражения — «T», тип результата — «указатель на T». ...
Для квалифицированного идентификатора ... Если член является нестатическим членом класса C типа T, тип результата — «указатель на член класса C типа T».

Согласно §5.1/7, B::b относится к случаю с квалифицированным идентификатором, а (B::b) — нет. Итак, компилятор интерпретирует это как lvalue.

person Ise Wisteria    schedule 16.02.2011

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

int*     foo()    { return &(B::b);}  // This is a pointer to an int


int A::* foo()    { return &B::b; }   // This is a pointer to a member of type int

Что вы хотите сделать, так это получить к нему доступ через объект A:

int A::* foo()    { return &A::b; }   // This is a pointer to a member of type int

Как и из A, вам разрешен доступ к нему.
Доступ к нему через B, подобный этому, является доступом извне и, таким образом, вызывает спецификаторы доступа.

person Martin York    schedule 16.02.2011
comment
Это лучше объясняет разницу, на мой взгляд. +1 - person Nawaz; 16.02.2011
comment
Доступ к нему через B, подобный этому, является доступом к нему извне и, таким образом, вызывает спецификаторы доступа. не правильно я считаю. Второй пример ОП доказывает это. Кроме того, представьте, что A содержит еще один атрибут с именем b shadowing B::b. Затем вы можете использовать &(A::b) и &(B::b), чтобы различать их. - person Tilman Vogel; 16.02.2011
comment
@Tilman Vogel: я не согласен. Вот почему выше я показал, как они различаются по тому, что на самом деле генерируется оператором and. - person Martin York; 17.02.2011