2

Consider following code:

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

My qestions is why we have access to protected methods/properties of base::Base from base::Base::Nested but no access to the same methods/properties of inherited::Base from inherited::Base::Nested?

The only thing I could suppose is that base::Base is a kind of global scope for base::Base::Nested thus they are accessible. inherited::Base is a kind of global scope for inherited::Base::Nested and protected members of base::Base are not accessible. However, public inheritance should not change scope of visibility and the reason of access inability is unclear for me.

ilardm
  • 146
  • 8
  • Your mind works so strangely. inherited::Base? Ok anyway, For the purposes of this example strip 80% of this code and make Base, BaseNested, Derived, and DerivedNested classes and get rid of all the functions that don't demonstrate your issue. – David Oct 13 '12 at 13:15
  • 2
    Please try to avoid the unnecessary cruft (namespaces, too many members) in your code and make it compilable (no strange undefined macros, no missing includes). Otherwise a good question. – pmr Oct 13 '12 at 13:52
  • Is there any reason to assume that this is not a duplicate of http://stackoverflow.com/questions/4672438/how-to-access-protected-method-in-base-class-from-derived-class? – jogojapan Oct 13 '12 at 14:19

2 Answers2

2

The issue seems to be the type of the stored pointer and not access rights. Consider this:

class B {
protected:
  void bar();
};

class D : public B {
public:
  void call() {
    this->bar(); // works
    static_cast<B*>(this)->bar(); // does not work
  }
};

The situation is similar when you try to call bar through the pointer that is stored in the base.

You can work around this issue by down-casting base, but I would strongly object to that.

pmr
  • 58,701
  • 10
  • 113
  • 156
  • 1
    Finally I get it. I'm trying to access to protected method `base::Base::bar()` from `inherited::Base::Nested::test()` because `base::Base::Nested::base`(and `inherited::Base::Nested::base`) is pointer of type `base::Base` and not of type `inherited::Base`. Now it is clear for me. – ilardm Oct 15 '12 at 05:03
  • 1
    @ilardm Please refer to the answer of jogojapan for the relevant standard quotes. This behavior seems unintuitive but is necessary so access restrictions cannot be trivially circumvented. – pmr Oct 15 '12 at 12:52
1

§11.4/1 has this to say on access to protected members (highlighting of relevant parts by me):

An additional access check beyond those described earlier in Clause 11 is applied when a non-static data member or non-static member function is a protected member of its naming class (11.2) As described earlier, access to a protected member is granted because the reference occurs in a friend or member of some class C. If the access is to form a pointer to member (5.3.1), the nested-name-specifier shall denote C or a class derived from C. All other accesses involve a (possibly implicit) object expression (5.2.5). In this case, the class of the object expression shall be C or a class derived from C.

This is not easy to interpret. Thankfully, the Standard gives a number of examples to illustrate the meaning, and one of them seems to be exactly your case (unless I misunderstood something in your code, which is quite possible):

(Note I have removed irrelevant parts of the examples, and renamed some elements for the sake of simplicity.)

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

In other words, in

pb->i = 1;

the member i is found through pb, a pointer to the base class, hence the naming class is B. But the access occurs from mem(), which is a member of D. B is not identical to D, nor is it derived from it (although D is derived from B), so the access is not allowed.

But in

i = 3

the member is found through this, hence the naming class is D and access is allowed.

jogojapan
  • 68,383
  • 11
  • 101
  • 131