Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Member access rules for friend of derived class, where the naming class is the base class

All standard references below refer, unless noted otherwise, to N4861 (March 2020 post-Prague working draft/C++20 DIS).


Background

According to [class.access.base]/5:

If a base class is accessible, one can implicitly convert a pointer to a derived class to a pointer to that base class [...].

Meaning the following example is well-formed:

class N {};

class P : private N {
    friend void f();
};

void f()  { 
    P p{};
    N* n = &p; // R: OK as per [class.access.base]/5
}

as N is an accessible base class at R above(+).

[class.access.base]/5 also mentions that [emphasis mine]:

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: [...] 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 denoted by the nested-name-specifier of the qualified-id (that is, T). — end note ]

and [emphasis mine]:

A member m is accessible at the point R when named in class N if

  • [...]
  • /5.3 m as a member of N is protected, and R occurs in a member or friend of class N, or in a member of a class P derived from N, where m as a member of P is public, private, or protected, or
  • /5.4 there exists a base class B of N that is accessible at R, and m is accessible at R when named in class B.

With that in mind, consider the following example:

class N {
  protected:
    int m;
};

class P : private N {
    friend void f();
};

void f()  {
    P p{};
    (&p)->N::m = 42;  // R: #1
}

where as per above the naming class at #1 is N. The example is accepted by both Clang and GCC, for various compiler versions and standards, meaning it's arguably well-formed.

It would seems as if &p (which is of type P*) is implicitly converted to N* (fulfilling [class.access.base]/6), but I'm wondering by what rules the member m of N (N being the naming class) is accessible at an R which is a friend to a derived class P of N.

Question

  • What rule(s) governs that #1 is well-formed?

As per above, the naming class at #1 is N, but [class.access.base]/5.3 should not apply as R is in a friend of class P derived from N (/5.3 only mentions in a member of class P). [class.access.base]/5.4 should not apply as the naming class is N which is the top-level class in the class hierarchy.

We may note that [class.protected]/1 mentions the very example above as well-formed as part of the non-normative example block of the paragraph. However, [class.protected]/1 in its entirety is described as

An additional access check [...]

arguably meaning [class.access.base] still needs to apply; it particularly seems as if [class.access.base]/5.3 is arguably missing mentioning the case "or a friend of class P" which [class.protected]/1 shows in an (non-normative) example.


(+) An accessible base class

In the following example:

class B { };

class N : B {
    friend void f();
};

void f()  { /* R */ }

according to [class.access.base]/4, specifically [class.access.base]/4.2 [emphasis mine]:

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

  • /4.1 [...]
  • /4.2 R occurs in a member or friend of class N, and an invented public member of B would be a private or protected member of P, [...]

B is accessible at R, namely in the friend f of N.

like image 831
dfrib Avatar asked Jan 26 '21 17:01

dfrib


People also ask

How do you access members of the base class from within a derived class?

You can use both a structure and a class as base classes in the base list of a derived class declaration: If the derived class is declared with the keyword class , the default access specifier in its base list specifiers is private .

Can base class access members of derived class give reasons?

Explanation: A base class pointer can point to a derived class object, but we can only access base class member or virtual functions using the base class pointer because object slicing happens when a derived class object is assigned to a base class object.

Can you access a protected member of a base class from a member function of the derived class derived in private mode?

Protected members in a class are similar to private members as they cannot be accessed from outside the class. But they can be accessed by derived classes or child classes while private members cannot.

Are protected members of a base class accessed in the derived class when inherited?

If a class is derived privately from a base class, all protected base class members become private members of the derived class. Class A contains one protected data member, an integer i . Because B derives from A , the members of B have access to the protected member of A .


Video Answer


1 Answers

Actually, the section [class.protected#1] is an additional clause for [class.access.base#5] when the nominated member is a protected member of the naming class to which R occurs at a member or friend of a derived class.
According to class.access.base#1, the non-static protected member of N is accessible as a private member of the derived class P. We are permitted to access the member m in a friend of P as per class.access.base#5.2, if the naming class is P

m as a member of N is private, and R occurs in a member or friend of class N, or

Back to [class.protected#1], we should take a look at the following rule:

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.

In other words, the rule says that only if these conditions are satisfied will the additional rule be applied. That is:

  1. The member should first be a non-static member(data or function)
  2. The member should be a protected member of the naming class.

In order to make the additional rule apply, the following conditions should also be satisfied

  1. As described earlier, access to a protected member is granted because the reference occurs in a friend or member of some class C.
  2. 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.
  3. All other accesses involve a (possibly implicit) object expression. In this case, the class of the object expression shall be C or a class derived from C.

Condition 3 might have some confusion, however, it does not say C must be the naming class or not. It just says that the member can be accessible in a member or friend of C.

After sorting out these conditions, we could take a look at the example

void f()  {
    P p{};
    (&p)->N::m = 42;  // R: #1
}

The naming class is N and the non-static member m is a protected of N, hence conditions 1 and 2 are true.

m as a private member is accessible in a friend of P, hence condition 3 is true.

(&p)->N::m is a class member access expression where its object expression is of type P, hence condition 5 is true. So, such an expression is well-formed when it occurs in f.

If change the non-static member m to be a static member, then condition 1 is false, which means the additional rule will not apply to the expression.

In simple, [class.protected#1] add an extra option(that is a friend) for the latter bullet of [class.access.base#5.2], if these conditions are satisfied. Moreover, it also restricts these members that might be accessible as per the latter bullet of [class.access.base#5.2] to turn non-accessible members when they satisfy these conditions.

like image 133
xmh0511 Avatar answered Oct 20 '22 01:10

xmh0511