Рассмотрим следующий код:
namespace base {
class Base {
protected:
class Nested {
protected:
Base* base;
public:
Nested(Base* _base) : base( _base ){}
virtual void test() {
base->foo();
/*
* hmm.. we can use protected methods
*/
base->bar();
}
};
protected:
Nested* nested;
void bar(){}
public:
void foo(){}
virtual void test() {
nested = new Nested(this);
nested->test();
}
};
};
namespace inherited {
class Base : public base::Base {
public:
Base()
: base::Base() {}
protected:
class Nested : public base::Base::Nested {
public:
Nested( inherited::Base* base )
: base::Base::Nested( base ) {}
public:
virtual void test() {
base->foo();
/*
* hmm.. and now they are not accessible
*/
// base->bar();
}
};
public:
virtual void test() {
foo();
bar();
nested = new Nested(this);
nested->test();
}
};
};
Мои вопросы, почему у нас есть доступ к защищенным методам / свойствам base::Base
от base::Base::Nested
но нет доступа к тем же методам / свойствам inherited::Base
от inherited::Base::Nested
?
Единственное, что я мог предположить, это то, что base::Base
это своего рода глобальный охват за base::Base::Nested
таким образом они доступны. inherited::Base
это своего рода глобальный охват за inherited::Base::Nested
и защищенные члены base::Base
не доступны. Однако публичное наследование не должно изменять область видимости, и причина невозможности доступа для меня неясна.
Кажется, проблема заключается в типе хранимого указателя, а не в правах доступа. Учти это:
class B {
protected:
void bar();
};
class D : public B {
public:
void call() {
this->bar(); // works
static_cast<B*>(this)->bar(); // does not work
}
};
Ситуация похожа, когда вы пытаетесь позвонить bar
сквозь
указатель, который хранится в базе.
Вы можете обойти эту проблему, опустив базу, но я бы категорически против этого.
В §11.4 / 1 говорится о доступе к защищенным членам (выделение мной соответствующих частей):
Дополнительная проверка доступа, помимо описанной ранее в разделе 11, применяется, когда нестатический элемент данных или функция нестатического члена является защищенным членом своего класса именования (11.2). Как описано ранее, доступ к защищенному члену предоставляется, потому что ссылка происходит у друга или члена некоторого класса C. Если доступ должен формировать указатель на член (5.3.1), спецификатор вложенного имени должен обозначать C или класс, производный от C. Все другие обращения включают (возможно, неявное) выражение объекта (5.2.5). В этом случае классом выражения объекта должен быть C или класс, производный от C.
Это нелегко интерпретировать. К счастью, в Стандарте приведено несколько примеров, иллюстрирующих значение, и один из них, похоже, является именно вашим случаем (если я не понял что-то в вашем коде, что вполне возможно):
(Обратите внимание, что я удалил ненужные части примеров и переименовал некоторые элементы для простоты.)
class B {
protected:
int i;
static int j;
};
class D : public B {
void mem(B*);
};void D::mem(B* pb) {
pb->i = 1; // ill-formed
i = 3; // OK (access through this)
/* The following cases are not directly relevant: */
B::i = 4; // OK (access through this, qualification ignored)
int B::* pmi_B = &B::i; // ill-formed
int B::* pmi_B2 = &D::i; // OK
j = 5; // OK (because j refers to a static member)
B::j = 6; // OK (because B::j refers to a static member)
}
Другими словами, в
pb->i = 1;
член i
найден через pb
указатель на базовый класс, следовательно, класс именования является B
, Но доступ происходит из mem()
, который является членом D
, B
не идентичен D
и не вытекает из этого (хотя D
происходит от B
), поэтому доступ запрещен.
Но в
i = 3
член найден через this
следовательно, класс именования D
и доступ разрешен.