SC22/WG21/N1218
J16/99-0042
22 October 1999
Thomas R Wilcox
trw@rational.com

 

Clarifying the Definition of an Accessible Base Class (Revision 3)

This document discusses problems with the specification of the concept of accessibility in the standard and proposes a possible resolution. The discussion is motivated by four items on the core issues list:

  1. Issue 9: Clarification of access to base class members
  2. Issue 16: Access to members of indirect private base classes

 Issues 9 and 16: Clarification of Access to Base Class Members

Committee document SC22/WG21/N1179 J16/99-0002 proposed extensive changes to clause 11 to resolve the logical circularity in the definition of accessible base types that exists in the standard and to generally improve the readability and robustness of that clause. After discussion at the Dublin meeting in April, 1999, it was decided to make only the changes necessary to resolve the issues exposed by the above defects. That second proposal, SC22/WG21/N1198 J16/99-0021, attempted to resolve the problem simply by adding wording that would prevent the circularity in the definition. Review at the Kona meeting revealed that approach to be flawed. This document reverts to the original approach, but limits changes to only those needed to resolve the defect.

The major problem with the current text is that the definitions of accessible base classes and accessible member of base classes are interdependent and circular. The definitions of accessible is complex and somewhat non-intuitive. The standard needs to provide a definition that the reader may grind through step-by-step to determine if a member or base class is accessible. Because the published definitions are circular, such a step-by-step procedure is currently not provided.

There are two changes. The first change is to reword the definition of accessible base class at the start of paragraph 4 so that it parallels the definition of an accessible member given at the end of paragraph 4. The second change is to add a constraint to the fourth bullet at the end of paragraph 4 to prevent infinite recursion of the definition.

After these changes, clause 11.2 paragraph 4 (now paragraphs 4 and 5) reads as follows (additions shown in red):

4- A base class B of N is accessible at R, if

class B {
public:
  int m;
};
class S : private B {
  friend class N;
};
class N : private S {
  void f() {
    B* p = this;   // OK: Because class S satisfies the fourth condition above...
                   // B is a base class of S accessible in f because f is a friend of S and m is private in S
                   // and S is a base class of N accessible in f because f is a member of N and a public
                   // member of S would be private in N.
  }
};

--- end example]

-5- A base class is said to be accessible if an invented public member of the base class is accessible. If a base class is accessible, one can implicitly convert a pointer to a derived class to a pointer to that base class (conv.ptr, conv.mem). [Note: it follows that members and friends of a class X can implicitly convert an X* to a pointer to a private or protected immediate base class of X. ] The access to a member is affected by the class in which the member is named. This naming class is the class in which the member name was looked up and found. [Note: this class can be explicit, e.g., when a qualified-id is used, or implicit, e.g., when a class member access operator (expr.ref) is used (including cases where an implicit ``this->'' is added). If both a class member access operator and a qualified-id are used to name the member (as in p->T::m), the class naming the member is the class named by the nested-name-specifier of the qualified-id (that is, T). If the member m is accessible when named in the naming class according to the rules below, the access to m is nonetheless ill-formed if the type of p cannot be implicitly converted to type T (for example, if T is an inaccessible base class of p's class). ]A member m is accessible at the point R when named in class N if

class B;
class A {
private:
  int i;
  friend void f(B*);
};
class B : public A { };
void f(B* p) {
  p->i = 1;                     //  OK:  B*  can be implicitly cast to  A*,
                     //  and  f  has access to  i  in  A
}


--- end example]

------------------------------------------------------------

[Discussion of issues....

Issue 9 Example:
    class D;
 
    class B
    {
     protected:
       int b1;
 
       friend void foo( D* pd );
    };
 
    class D : protected B { };
 
    void foo( D* pd )
    {
       if ( pd->b1 > 0 ); // Is 'b1' accessible?
    }

Analysis: Is b1 accessible?

Issue 16 Example:

  class D;
 
  class B {
  private:
      int i;
      friend class D;
  };
 
  class C : private B { };
 
  class D : private C {
      void f() {
          B::i; //1: well-formed?
          i;    //2: well-formed?
      }
  };

(A) Is i accessible in f when named in B?

(B) Is i accessible in f when named in D?

So, in both these cases, friendship with the base class does not grant access to the private member because the base class is not accessible through two levels of private base classes. If, however, B were merely a protected base of C rather than a private base of C, then i would be accessible within f because B would be an accessible base of D within f (f is a member of D and a public member of B would be a private member of D) and it has already been established that i is accessible within f when named in B. The B:: qualification is redundant in this case.