为啥派生类的朋友不能使用受保护的成员?

Posted

技术标签:

【中文标题】为啥派生类的朋友不能使用受保护的成员?【英文标题】:Why can’t protected members be used by friends of derived classes?为什么派生类的朋友不能使用受保护的成员? 【发布时间】:2020-05-27 10:50:55 【问题描述】:

[class.access/1] 中的 C++ 标准状态(强调我的):

一个类的成员可以是

私人;也就是说,它的名称只能被声明它的类的成员和朋友使用。 受保护;也就是说,它的名称只能由声明它的类的成员和朋友、从该类派生的类、及其朋友使用(参见 [class.protected])。 公开;也就是说,它的名称可以在任何地方使用而不受访问限制。

那么为什么编译器会在下面的 C++ 程序中引发这个错误呢?

#include <iostream>

class B 
  protected:
    static int const i = 1;
;

class D: public B 
  public:
    void f();
    friend void g();
;

void D::f() 
  B b;
  std::cout << b.i;  // OK


void g() 
  B b;
  std::cout << b.i;  // error: 'i' is a protected member of 'B'


int main() 
  D d;
  d.f();
  g();
  return 0;

请注意,受保护的数据成员 B::i 被声明为 static 不受 [class.access/class.protected-1] 中受保护的非静态成员的进一步限制,这将在D::f 成员函数中的b.i 访问也会引发与g 函数中相同的错误。

注意。 — 我在 Clang 9.0.0 编译器上使用 C++ 17。

【问题讨论】:

仅供参考失败; g++ 和 MSVC 编译 - 实时:godbolt.org/z/TkmnbW 您可能需要添加 [language-lawyer] 标签 另外,如果你定义 g 内联(并稍微修改它以便可以通过 ADL 调用)然后 clang 也会编译:godbolt.org/z/_2rEdA 对我来说似乎是一个 clang 错误。 【参考方案1】:

这似乎是规格问题。我引用的部分与指定 Clang 之后的新正确行为的另一部分不同步(但令人惊讶的是,GCC 和 MSVC 都不是)[class.access/base-5](强调我的):

如果在类 N 中命名,成员 m 可在点 R 访问

m 作为 N 的成员是公开的,或者 m 作为 N 的成员是私有的,而 R 出现在 N 类的成员或朋友中,或者 m 作为 N 的成员是受保护的,并且 R 出现在类 N 的成员或朋友中,或在派生自 N 的类 P 的成员中,其中 m 作为 P 的成员是公共的、私有的或受保护的,或
存在可在 R 访问的 N 的基类 B,当在类 B 中命名时,m 可在 R 访问。

这里friend没有提到派生类。但它曾经是。由于缺陷报告CWG 1873(强调我的),它已在 C++ 17 中被删除:

受保护的派生类朋友的成员访问

部分:14.2 [class.access.base] 状态:CD4 提交者: 理查德·史密斯日期:2014-02-18

[于 2015 年 5 月移至 DR 会议。]

根据 14.2 [class.access.base] 第 5 段,

如果在类 N 中命名,成员 m 可在点 R 访问

… m 作为 N 的成员受到保护,R 出现在 类 N,或在派生自 N 的类 P 的成员或朋友中,其中 m 作为 P 的成员是公共的、私有的或受保护的,或 …

通过类 P 授予访问权限令人不安。至少,有 应该是 P 在 R 处可见的限制。或者,这 可以完全删除部分规则;这项规定确实 似乎没有在现有代码中广泛使用,并且此类引用可以 可以很容易地转换为使用 P 而不是 N 来命名成员。

2014 年 6 月会议记录:

CWG 指出,删除好友条款将引入 P 及其朋友的成员函数之间存在不希望的不对称性。 相反,其目的是要求 P 是 R 的完整类型。

2014 年 11 月会议记录:

虽然不对称是不幸的,但 成员函数中的引用和朋友中的引用是 成员函数具体决定了哪个 P 在视野范围内,而 朋友可以通过一个专业化的类成为朋友 类模板,因此需要实例化 否则发生。因此,CWG 决定简单地消除友方案件。

提议的决议,2014 年 11 月:

将 14.2 [class.access.base] 的第 5.3 项更改如下:

如果在类 N 中命名,成员 m 可在点 R 访问

… m 作为 N 的成员受到保护,R 出现在 类 N,或在派生自 N 的类 P 的成员 或朋友中,其中 m 作为 P 的成员是公共的、私有的或受保护的,或 存在……

我已在 GitHub 上提交拉取请求以更正此不一致:https://github.com/cplusplus/draft/pull/3672

【讨论】:

同意DR的决议,这样颠覆访问控制似乎是假的

以上是关于为啥派生类的朋友不能使用受保护的成员?的主要内容,如果未能解决你的问题,请参考以下文章

C++ 派生模板类:访问实例的受保护成员

protectedpublicprivate

为什么派生类d的对象不能调用基类的受保护成员函数?

无法访问派生类中基类的受保护成员

为啥我不能访问派生构造函数的成员初始化列表中继承的受保护字段?

C++ 派生类访问属性