为什么派生类的友元无法访问基类中的保护成员?

11
[class.access/1]中,C++标准规定(强调我的):
一个类的成员可以是:
- private:只有在该类中声明的成员和友元才能使用它的名称。 - protected:只有在该类中声明的成员和友元,以及从该类派生的类和它们的友元(见[class.protected])才能使用它的名称。 - public:即其名称可在任何地方使用,没有访问限制。
那么为什么编译器会在以下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被声明为静态,以免受到特定于受保护的非静态成员的进一步限制,在[class.access/class.protected-1]中也会在D::f成员函数和g函数中的b.i访问处引发相同的错误。 注意。 — 我正在使用Clang 9.0.0编译器上的C++ 17。

3
FYI,Clang编译失败;而g++和MSVC可以编译 - 在此链接中查看:https://godbolt.org/z/TkmnbW。您可能需要添加[language-lawyer]标签。 - Richard Critten
此外,如果您在内联定义 g(并稍微修改它以便通过 ADL 调用),那么 clang 也会编译:https://godbolt.org/z/_2rEdA 对我来说看起来像是一个 clang 的 bug。 - N. Shead
1个回答

5
这似乎是一个规范问题。我引用的部分与另一部分不同步,后者指定了Clang遵循的新正确行为(但令人惊讶的是GCC和MSVC没有),[class.access/base-5](强调我的):
“当在类N中命名时,成员m在点R可访问,如果:
  • 作为N的成员,m是公共的;或
  • 作为N的成员,m是私有的,并且R出现在类N的成员或友元中;或
  • 作为N的成员,m是受保护的,并且R出现在类N的成员或友元中,或者出现在从N派生的类P的成员或友元中,其中m作为P的成员是公共的、私有的或受保护的;或
存在一个基类B是N的可访问的,在R处命名类B中的m时,m在R处是可访问的。”

这里没有提到派生类的 friend 。但以前有过。它在C ++ 17中被删除,因为存在缺陷报告CWG 1873(重点是我的):

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

章节: 14.2 [class.access.base] 状态: CD4 提交者: Richard Smith 日期: 2014-02-18

[已在2015年5月转移到DR。]

根据14.2 [class.access.base]第5段,如果m作为N的成员是受保护的,并且R出现在类N的成员或友元中,或者出现在从N派生的类P的成员或友元中,其中m作为P的成员是公共的、私有的或受保护的,那么在命名类N时,成员m在点R处是可访问的。

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

2014年6月会议的备注:

CWG指出,如果删除友元条款,将会在P的成员函数和其友元之间引入不必要的不对称性。相反,意图是要求P在R处是一个完整的类型。

2014年11月会议的备注:

虽然不对称性很不幸,但成员函数和友元之间的差异在于,成员函数具体确定了所看到的P,而友元可能被一个类的特化给赋予权限,这将需要未曾发生的实例化。因此,CWG决定简单地消除友元情况。

建议的解决方案,2014年11月:

将14.2 [class.access.base]第5.3条改为:

如果m作为N的成员是受保护的,并且R出现在类N的成员或友元中,或者出现在从N派生的类P的成员中,在命名类N时,成员m在点R处是可访问的,其中m作为P的成员是公共的、私有的或受保护的。

  • 存在……

我在 GitHub 上提交了一个 pull request 以纠正这个不一致性: https://github.com/cplusplus/draft/pull/3672


同意DR决议,访问控制可以被这种方式破坏似乎是虚假的。 - M.M

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接