派生 class 的朋友的成员访问规则,其中命名 class 是基础 class
Member access rules for friend of derived class, where the naming class is the base class
除非另有说明,否则以下所有标准参考均指 N4861 (March 2020 post-Prague working draft/C++20 DIS)。
背景
If a base class is accessible, one can implicitly convert a pointer to a derived class to a pointer to that base class [...].
表示以下示例格式正确:
class N {};
class P : private N {
friend void f();
};
void f() {
P p{};
N* n = &p; // R: OK as per [class.access.base]/5
}
因为 N
是可访问的基础 class 在 R
以上 (+).
[class.access.base]/5 还提到 [强调 我的]:
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
]
和[强调我的]:
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
.
考虑到这一点,请考虑以下示例:
class N {
protected:
int m;
};
class P : private N {
friend void f();
};
void f() {
P p{};
(&p)->N::m = 42; // R: #1
}
根据上面的 命名 class 在 #1
是 N
。该示例被 Clang 和 GCC 接受,适用于各种编译器版本和标准,这意味着它可以说是格式良好的。
似乎 &p
(属于 P*
类型)隐式转换为 N*
(满足 [class.access.base]/6),但我想知道什么规则 N
的成员 m
(N
是命名 class)可在 R
访问,该 R
是派生 [=148= 的朋友] P
共 N
。
问题
- 什么规则规定
#1
是合式的?
根据上述,命名 class 在 #1
是 N
,但 [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 不应适用,因为命名 class 是 N
,这是 class 层次结构中的顶级 class。
我们可能会注意到 [class.protected]/1 提到上面的示例作为该段落的非规范示例块的一部分格式良好。然而,[class.protected]/1 整体被描述为
An additional access check [...]
可以说是[class.access.base]仍然需要申请;特别是 [class.access.base]/5.3 似乎缺少提及案例“或 class P
的朋友”,[class.protected]/1 在 (非规范)示例。
(+) 可访问的基础 class
在下面的例子中:
class B { };
class N : B {
friend void f();
};
void f() { /* R */ }
根据[class.access.base]/4, specifically [class.access.base]/4.2[强调我的]:
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
可在 R
访问,即 N
的朋友 f
。
实际上,[class.protected#1] 部分是 [class.access.base#5] 的附加条款,当指定成员是 命名 class 其中 R 出现在派生 class.
的成员或朋友处
根据class.access.base#1,
N
的非静态受保护成员可作为派生 class P
的私有成员访问。如果命名 class 是 P
,我们可以根据 class.access.base#5.2 访问 P
的朋友中的成员 m
m as a member of N is private, and R occurs in a member or friend of class N, or
回到[class.protected#1],我们应该看看下面的规则:
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.
换句话说,规则说只有满足这些条件才会应用附加规则。即:
- The member should first be a non-static member(data or function)
- The member should be a protected member of the naming class.
为了使附加规则适用,还应满足以下条件
- 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.
条件3可能有些混淆,但是,它并没有说C
一定是命名class或不是。它只是说该成员可以在 C
.
的成员或朋友中访问
整理完这些条件,我们可以看一下例子
void f() {
P p{};
(&p)->N::m = 42; // R: #1
}
命名 class 是 N,非静态成员 m
是 N
的受保护成员,因此条件 1 和条件 2 成立。
m
因为私有成员可以在 P
的好友中访问,因此条件 3 成立。
(&p)->N::m
是一个 class 成员访问表达式,其对象表达式的类型为 P,因此条件 5 为真。因此,当出现在 f
.
中时,这样的表达式是合式的
如果将非静态成员 m
更改为静态成员,则条件 1 为假,这意味着附加规则将不适用于表达式。
简单来说,如果满足这些条件,[class.protected#1]为[class.access.base#5.2]的后一个项目符号添加一个额外的选项(即朋友)。此外,它还限制了这些根据[class.access.base#5.2]的后一条可以访问的成员在满足这些条件时变为不可访问的成员。
除非另有说明,否则以下所有标准参考均指 N4861 (March 2020 post-Prague working draft/C++20 DIS)。
背景
If a base class is accessible, one can implicitly convert a pointer to a derived class to a pointer to that base class [...].
表示以下示例格式正确:
class N {};
class P : private N {
friend void f();
};
void f() {
P p{};
N* n = &p; // R: OK as per [class.access.base]/5
}
因为 N
是可访问的基础 class 在 R
以上 (+).
[class.access.base]/5 还提到 [强调 我的]:
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 ]
和[强调我的]:
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
.
考虑到这一点,请考虑以下示例:
class N {
protected:
int m;
};
class P : private N {
friend void f();
};
void f() {
P p{};
(&p)->N::m = 42; // R: #1
}
根据上面的 命名 class 在 #1
是 N
。该示例被 Clang 和 GCC 接受,适用于各种编译器版本和标准,这意味着它可以说是格式良好的。
似乎 &p
(属于 P*
类型)隐式转换为 N*
(满足 [class.access.base]/6),但我想知道什么规则 N
的成员 m
(N
是命名 class)可在 R
访问,该 R
是派生 [=148= 的朋友] P
共 N
。
问题
- 什么规则规定
#1
是合式的?
根据上述,命名 class 在 #1
是 N
,但 [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 不应适用,因为命名 class 是 N
,这是 class 层次结构中的顶级 class。
我们可能会注意到 [class.protected]/1 提到上面的示例作为该段落的非规范示例块的一部分格式良好。然而,[class.protected]/1 整体被描述为
An additional access check [...]
可以说是[class.access.base]仍然需要申请;特别是 [class.access.base]/5.3 似乎缺少提及案例“或 class P
的朋友”,[class.protected]/1 在 (非规范)示例。
(+) 可访问的基础 class
在下面的例子中:
class B { };
class N : B {
friend void f();
};
void f() { /* R */ }
根据[class.access.base]/4, specifically [class.access.base]/4.2[强调我的]:
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
可在 R
访问,即 N
的朋友 f
。
实际上,[class.protected#1] 部分是 [class.access.base#5] 的附加条款,当指定成员是 命名 class 其中 R 出现在派生 class.
的成员或朋友处
根据class.access.base#1,
N
的非静态受保护成员可作为派生 class P
的私有成员访问。如果命名 class 是 P
P
的朋友中的成员 m
m as a member of N is private, and R occurs in a member or friend of class N, or
回到[class.protected#1],我们应该看看下面的规则:
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.
换句话说,规则说只有满足这些条件才会应用附加规则。即:
- The member should first be a non-static member(data or function)
- The member should be a protected member of the naming class.
为了使附加规则适用,还应满足以下条件
- 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.
条件3可能有些混淆,但是,它并没有说C
一定是命名class或不是。它只是说该成员可以在 C
.
整理完这些条件,我们可以看一下例子
void f() {
P p{};
(&p)->N::m = 42; // R: #1
}
命名 class 是 N,非静态成员 m
是 N
的受保护成员,因此条件 1 和条件 2 成立。
m
因为私有成员可以在 P
的好友中访问,因此条件 3 成立。
(&p)->N::m
是一个 class 成员访问表达式,其对象表达式的类型为 P,因此条件 5 为真。因此,当出现在 f
.
如果将非静态成员 m
更改为静态成员,则条件 1 为假,这意味着附加规则将不适用于表达式。
简单来说,如果满足这些条件,[class.protected#1]为[class.access.base#5.2]的后一个项目符号添加一个额外的选项(即朋友)。此外,它还限制了这些根据[class.access.base#5.2]的后一条可以访问的成员在满足这些条件时变为不可访问的成员。