dfr*_*fri 6 c++ language-lawyer
除非另有说明,否则以下所有标准参考均指N4861(2020 年 3 月布拉格后工作草案/C++20 DIS)。
背景
如果基类是可访问的,则可以将指向派生类的指针隐式转换为指向该基类的指针 [...]。
这意味着以下示例是格式良好的:
class N {};
class P : private N {
friend void f();
};
void f() {
P p{};
N* n = &p; // R: OK as per [class.access.base]/5
}
Run Code Online (Sandbox Code Playgroud)
asN是R上面(+)处的可访问基类。
[class.access.base]/5 还提到[强调我的]:
对成员的访问受命名该成员的类的影响。此命名类是在其中查找并找到成员名称的类。[注: [...]如果两个类成员访问运算符和一个合格-ID被用于命名构件(如在
p->T?::?m),类命名构件是由合格的嵌套名称说明符所表示的类-id(即,T)。—尾注 ]
和[强调我的]:
一个成员在类中命名时
m可以访问,如果RN
- [...]
- /5.3
m作为的成员N是受保护的,并且R出现在类的成员或朋友中N,或P派生自的类的成员中N,其中m作为 的成员P是公共的、私有的或受保护的,或- /5.4存在一个基类
B的N也就是在访问R,并m在访问R时,在课堂命名B。
考虑到这一点,请考虑以下示例:
class N {
protected:
int m;
};
class P : private N {
friend void f();
};
void f() {
P p{};
(&p)->N::m = 42; // R: #1
}
Run Code Online (Sandbox Code Playgroud)
其中按照上述命名类在#1IS N。该示例被 Clang 和 GCC 接受,适用于各种编译器版本和标准,这意味着它可以说是格式良好的。
那就好像&p(这是类型P*)隐式转换N*(履行[class.access.base / 6),但我用什么样的规则成员想知道m的N(N被命名类)可访问在R其是 的派生类P的朋友N。
题
#1良好格式?按照上述,在命名类在#1是N,但[class.access.base] /5.3不应作为应用R是在朋友类的P衍生自N(/5.3只提到在一个构件类的P)。[class.access.base]/5.4不应适用,因为命名类是N类层次结构中的顶级类。
我们可能会注意到[class.protected]/1提到了上面的例子,作为段落的非规范示例块的一部分。但是, [class.protected]/1 的整体描述为
额外的访问检查 [...]
可以说意味着 [class.access.base] 仍然需要申请;似乎 [class.access.base]/5.3 似乎没有提到P[class.protected]/1 在(非规范性)示例中显示的“或类的朋友”的情况。
(+) 一个可访问的基类
在以下示例中:
class B { };
class N : B {
friend void f();
};
void f() { /* R */ }
Run Code Online (Sandbox Code Playgroud)
根据[class.access.base]/4,特别是[class.access.base]/4.2 [强调我的]:
基类
B的N是访问R,如果
- /4.1 [...]
- /4.2
R出现在类的成员或朋友中N,并且发明的公共成员B将是 的私有或受保护成员P,[...]
B可在R,即在 的朋友f中访问N。
实际上,当指定成员是命名类的受保护成员(R 出现在派生类的成员或友元处)时,[class.protected#1] 部分是 [class.access.base#5] 的附加子句。
根据class.access.base#1, 的非静态 protected 成员N可以作为派生类的私有成员进行访问P。如果命名类是,我们可以按照class.access.base#5.2m访问友元中的成员PP
m 作为 N 的成员是私有的,并且 R 出现在 N 类的成员或友元中,或者
回到[class.protected#1],我们应该看一下以下规则:
当非静态数据成员或非静态成员函数是其命名类([class.access.base])的受保护成员时,将应用超出条款 [class.access] 中先前描述的附加访问检查115 如所述早些时候,授予对受保护成员的访问权限,因为引用发生在某个 C 类的友元或成员中。
换句话说,该规则规定,只有满足这些条件,才会应用附加规则。那是:
- 该成员首先应该是非静态成员(数据或函数)
- 该成员应该是命名类的受保护成员。
为了使附加规则适用,还应满足以下条件
- 如前所述,授予对受保护成员的访问权限是因为引用发生在某个类 C 的友元或成员中。
- 如果访问是为了形成指向成员 ([expr.unary.op]) 的指针,则嵌套名称说明符应表示 C 或从 C 派生的类。
- 所有其他访问都涉及(可能是隐式的)对象表达式。在这种情况下,对象表达式的类应为 C 或从 C 派生的类。
条件3可能有些混乱,但是它并没有说C一定是命名类。它只是说可以在 的会员或朋友中访问该会员C。
理清了这些条件之后,我们就可以看一下例子了
void f() {
P p{};
(&p)->N::m = 42; // R: #1
}
Run Code Online (Sandbox Code Playgroud)
命名类是 N 并且非静态成员m是 protected of N,因此条件 1 和 2 为真。
m由于 的 好友可以访问私有成员P,因此条件 3 为真。
(&p)->N::m是一个类成员访问表达式,其对象表达式的类型为 P,因此条件 5 为真。因此,当这样的表达式出现在 中时,它是格式良好的f。
如果将非静态成员更改m为静态成员,则条件 1 为 false,这意味着附加规则将不适用于表达式。
简单来说,如果满足这些条件,[class.protected#1] 会为 [class.access.base#5.2] 的后一个项目符号添加一个额外的选项(即朋友)。此外,它还限制这些可以根据 [class.access.base#5.2] 的后一个项目符号访问的成员,以在满足这些条件时将其转变为不可访问的成员。
| 归档时间: |
|
| 查看次数: |
130 次 |
| 最近记录: |