C++类中定义的友元函数的成员访问控制

nic*_*kie 6 c++ g++ friend-function language-lawyer clang++

我知道以下 C++ 代码片段应该在 的定义中产生错误g,因为它p.t.x是私有的,无法在那里访问。

class P {
  class T {
    int x;
    friend class P;
  };
  T t;

  friend void g(P &p);
};

void g(P &p) { p.t.x = 42; }
Run Code Online (Sandbox Code Playgroud)

令我困惑的是下一个片段。不同之处仅在于朋友函数的定义g现在出现class 中P

class P {
  class T {
    int x;
    friend class P;
  };
  T t;

  friend void g(P &p) { p.t.x = 42; }
};
Run Code Online (Sandbox Code Playgroud)

Clang++(6.0.0-1ubuntu2 和 Apple 版本 clang-1100.0.33.8)编译后者没有错误,而 GNU C++(7.5.0-3ubuntu1~18.04)产生与前一个片段相同的错误。

我知道g后一种情况中定义的函数与前一种情况中定义的函数不在同一范围内(参见相关问题较旧的较长讨论),并且只能通过 ADL 看到。但我认为我在这里问的是不同的:friend class P类中的声明是否应该T扩展到友元函数的主体g

C++ 标准的相关部分(在最近的草案中为§11.3 或 §11.9.3 )指出:

7 ... 在类中定义的友元函数在定义它的类的(词法)范围内。在类外定义的友元函数不是 (6.5.1)。

所以我知道 Clang++ 和 GNU C++ 对“词法范围”的含义有不同的解释(另请参阅上一个相关问题的答案)。锵++看起来编译g好像它是类的朋友T,可能是因为它在类的词汇范围,P这是类的朋友T,而GNU C ++不。

  1. 两个编译器之一中是否存在错误?
  2. 如果是,是哪一个?
  3. 不管前面问题的答案如何,这不是标准应该更好地形式化的东西吗?

bar*_*top 0

对于初学者 - 免责声明:这是我对标准的解释,尽管我认为这是正确的,但人们无法确定真正的含义。

太长了;博士

  1. 是的。
  2. 国际海事组织铿锵++
  3. 如果你看下面,你会发现它的形式化得非常好。

完整答案

话虽如此,我认为该标准中有两个相关的引用。首先是这样的:

11.9 会员访问控制

1 类的成员可以是

(1.1) - 私人;也就是说,它的名称只能由声明它的类的成员和友元使用。

第二个是:

11.9.3 朋友[class.friend]

1 类的友元是被授予使用类中的私有和受保护成员名称的权限的函数或类。类通过友元声明来指定其友元(如果有)。此类声明为好友提供了特殊的访问权限,但并不使指定的好友成为好友类的成员

从这两点可以推断出一些事情。但最重要的是,即使是内联友元也无法访问友元类中定义的类的私有成员。为什么?因为它既不是嵌套类的成员也不是友元。由此看来,g++ 就在这里。