【发布时间】:2021-01-26 17:10:21
【问题描述】:
除非另有说明,否则以下所有标准参考均指N4861 (March 2020 post-Prague working draft/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
}
因为N 是上面R 的可访问基类(+)。
[class.access.base]/5 还提到 [emphasis mine]:
对成员的访问受该成员所在的类的影响 命名。 此命名类是成员名称所在的类 抬头发现。 [ 注意: [...] 如果两个类成员都访问 运算符和限定 ID 用于命名成员(如
p->T::m),命名该成员的类是由 限定 ID 的嵌套名称说明符(即T)。 — 结束说明 ]
和[强调我的]:
成员
m可在R点访问 当在类中命名时Nif
- [...]
- /5.3
m作为成员N受保护,并且R出现在N类的成员或朋友中,或在派生类P的成员 来自N,其中m作为P的成员是public、private 或 受保护,或- /5.4 存在
N的基类B,可在R访问,当在类B中命名时,m可在R访问。
考虑到这一点,考虑以下示例:
class N {
protected:
int m;
};
class P : private N {
friend void f();
};
void f() {
P p{};
(&p)->N::m = 42; // R: #1
}
#1 的 命名类 是N。该示例被 Clang 和 GCC 接受,适用于各种编译器版本和标准,这意味着它可以说是格式良好的。
似乎&p(类型为P*)隐式转换为N*(实现[class.access.base]/6),但我想知道@987654358 的成员m 的规则是什么@(N 是命名类)可通过 R 访问,这是 N 的派生类 P 的朋友。
问题
- 哪些规则规定
#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 可以说没有提到 [class.protected]/1 在(非规范)示例中显示的“或P类的朋友”的情况。
(+) 一个可访问的基类
在以下示例中:
class B { };
class N : B {
friend void f();
};
void f() { /* R */ }
根据[class.access.base]/4,特别是[class.access.base]/4.2 [强调我的]:
N的基类B可通过R访问,如果
- /4.1 [...]
- /4.2
R发生在成员或班级朋友N,B的发明公共成员将是私有成员或受保护成员P,[...]
B 可通过R 访问,即在N 的朋友f 中。
【问题讨论】:
-
在我看来
class N{ protected: int m;};class P:private N{ void fun(){ N& rf = *this; rf.m = 1;} };其中rf.m = 1应该按照 [class.access.base#5.3] 格式正确,因为 R 在 成员函数中 P.
标签: c++ language-lawyer