我知道以下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; }
让我感到困惑的是下一个片段。 它的不同之处仅在于友元函数的定义现在g
发生在类P
中。
class P {
class T {
int x;
friend class P;
};
T t;
friend void g(P &p) { p.t.x = 42; }
};
Clang++(6.0.0-1ubuntu2 和 Apple 版本 clang-1100.0.33.8)编译后者没有错误,而 GNU C++ (7.5.0-3ubuntu1~18.04) 产生与前一个代码段相同的错误。
我知道在后一种情况下定义的函数g
不在同一个范围内 作为前者中定义的那个(参见相关问题和较早的较长讨论),并且只能通过 ADL 看到。 但我认为我在这里问的是不同的:类T
friend class P
的声明是否应该扩展到朋友函数的主体g
?
C++标准的相关部分(最新草案中的§11.3或§11.9.3)指出:
7 ...类中定义的友元函数在(词法)范围内 定义它的类。在外部定义的友元函数 该类不是 (6.5.1)。
所以我理解 Clang++ 和 GNU C++对"词汇范围"的含义有不同的解释(另请参阅对上一个相关问题的回答)。 Clang++似乎编译g
,就好像它是类T
的朋友一样,可能是因为它在类P
的词汇范围内,它是类T
的朋友,而GNU C++则不是。
- 两个编译器之一中是否存在错误?
- 如果是,是哪一个?
- 不管前面问题的答案如何,这不是标准应该更好地形式化吗?
这看起来像CWG1699(仍然打开)。
1699.与班级交朋友会成为朋友吗?
根据14.3 [类友]第2段,
将类声明为好友意味着可以在好友类的基本说明符和成员声明中访问授予友谊的类中的私有和受保护成员的名称。
friend
声明是成员声明,但不清楚在朋友声明中授予友谊的程度。例如:class c { class n {}; friend struct s; }; struct s { // #1 and #2 are not relevant for this question friend void f() { c::n(); } // #3 };
特别是,如果在类定义中定义了 friend 函数,如 #3 所示,则其定义是否可以访问 befriending 类的私有和受保护成员?在这一点上,实现各不相同。
对于初学者 - 免责声明:这是我对标准的解释,即使我认为这是正确的,也无法确定真正的含义。
tl;博士
- 是的。
- 国际海事组织叮当声++
- 如果你看下面,你会发现它的形式化得非常好。
完整答案
话虽如此,我认为标准中有两个相关的引用。首先是这个:
11.9 成员访问控制
1 类的成员可以是
(1.1) - private;也就是说,它的名称只能由声明它的类的成员和朋友使用。
第二个是:
11.9.3 朋友
1 类的友元是被授予使用类中的私有和受保护成员名称的权限的函数或类。 类通过友元声明指定其友元(如果有)。 这样的声明赋予朋友特殊的访问权限,但它们不会使被提名的朋友成为朋友阶层的成员。
从这两个人可以推断出一些东西。但最重要的是,即使是内联好友也无法访问在友生类中定义的类的私人成员。为什么?因为它既不是嵌套类的成员,也不是嵌套类的朋友。由此看来,g++就在这里。