Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ nested struct inheritance rules (access to protected member) [duplicate]

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?

like image 322
Rupert Swarbrick Avatar asked Jan 17 '17 14:01

Rupert Swarbrick


1 Answers

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 ]

like image 150
songyuanyao Avatar answered Nov 15 '22 19:11

songyuanyao