10

Can anyone explain to me why (as in, "why is the language this way?") the following code has a compile error at the second line of B::C::bar?

class A
{
public:
    struct D
    {
        void call_foo (A &a)
        {
            a.foo ();
        }
    };

protected:
    void foo () {}
};

class B : public A
{
    struct C : public A::D
    {
        void bar (A &a, B &b)
        {
            b.foo (); // OK
            a.foo (); // Error. Huh?
            call_foo (a); // Ugly workaround
        }
    };
};

It seems that a method can safely use a protected method in a parent class if and only if the type of the base pointer is exactly the enclosing type (rather than some parent type).

This seems kind of odd. Why is the language that way?

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • imagine that A& is from completely different class hierarchy that has nothing to do with B, giving access to it from C would break every encapsulation idea – Oleg Bogdanov Jan 17 '17 at 15:17
  • 1
    This is rather misterious to me. Check, for instance: http://stackoverflow.com/a/1414520/1254880 where there's a quote from Bjarne Stroustrup saying that *A derived class can access a base class’ protected members only for objects of its own type.... This prevents subtle errors that would otherwise occur when one derived class corrupts data belonging to other derived classes.* But still doesn't help much, does it? – Tarc Jan 17 '17 at 15:42
  • This question (that I asked) is basically a duplicate of the question in the comment above. What's more, I don't think the single answer really answers it. Please could someone with the required super-powers close this as a duplicate? – Rupert Swarbrick Jan 17 '17 at 16:37

1 Answers1

8

The struct C is nested inside class B, it's regared as a member so it has the same access rights as any other member. So yes it could access the protected members of the base class A. But note that you could only access the protected members of A through an object of type B; you can't do that through a A. That makes sense, because the members of derived class should only be possible to access the protected members inherited from the base class; these members belong to the derived class. But accessing the protected members of the base class directly should not be allowed; they belong to the base class (or the other derived class).

The rule is not special for the inner class, it's also true for the member functions of B.

§11.4/1 Protected member access [class.protected]

(emphasis mine)

An additional access check beyond those described earlier in Clause [class.access] is applied when a non-static data member or non-static member function is a protected member of its naming class ([class.access.base])115 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 ([expr.unary.op]), the nested-name-specifier shall denote C or a class derived from C. All other accesses involve a (possibly implicit) object expression ([expr.ref]). In this case, the class of the object expression shall be C or a class derived from C. [ Example:

class B {
protected:
  int i;
  static int j;
};

class D1 : public B {
};

class D2 : public B {
  friend void fr(B*,D1*,D2*);
  void mem(B*,D1*);
};

...

void D2::mem(B* pb, D1* p1) {
  pb->i = 1;                    // ill-formed
  p1->i = 2;                    // ill-formed
  i = 3;                        // OK (access through this)
  B::i = 4;                     // OK (access through this, qualification ignored)
  int B::* pmi_B = &B::i;       // ill-formed
  int B::* pmi_B2 = &D2::i;     // OK
  j = 5;                        // OK (because j refers to static member)
  B::j = 6;                     // OK (because B​::​j refers to static member)
}

...

 — end example ]

songyuanyao
  • 169,198
  • 16
  • 310
  • 405