Reputation: 73186
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 pointR
when named in classN
if
- [...]
- /5.3
m
as a member ofN
is protected, andR
occurs in a member or friend of classN
, or in a member of a classP
derived fromN
, wherem
as a member ofP
is public, private, or protected, or- /5.4 there exists a base class
B
ofN
that is accessible atR
, andm
is accessible atR
when named in classB
.
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
#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
ofN
is accessible atR
, if
- /4.1 [...]
- /4.2
R
occurs in a member or friend of classN
, and an invented public member ofB
would be a private or protected member ofP
, [...]
B
is accessible at R
, namely in the friend f
of N
.
Upvotes: 6
Views: 185
Reputation: 7369
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:
- The member should first be a non-static member(data or function)
- 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
- 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. 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.
Upvotes: 2