C++中的内部类会自动成为友元吗?

86

如果我在C++中定义一个内部类,它会自动成为包含它的类的友元吗?例如,这样做是否合法:

class Outer {
public:
    class Inner {
    public:
        void mutateOuter(Outer& o);
    };

private:
    int value;
};

void Outer::Inner::mutateOuter(Outer& o) {
    o.value ++; // Legal?  Or not?
}

我之所以问这个问题是因为在一些编译器中(比如VS2003)这段代码不能正常工作,但是我听说至少有一些编译器可以。 我找不到C ++规范中相关的部分,如果有人能引用具体内容来说明它是否合法,那就太好了。


嵌套类的成员函数遵循常规访问规则,并且对其封闭类的成员没有特殊访问权限。:http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8a.doc%2Flanguage%2Fref%2Fcplr061.htm - Anycorn
1
@aaa- 感谢提供链接,但这似乎只适用于IBM的编译器,我知道它在规范上有一些自由(例如,允许您使用“&&”运算符获取标签的地址)。如果我对此事过于严格,请原谅,但我教授C++编程课程,希望在告诉我的学生之前非常确定答案。 - templatetypedef
4
顺便说一下,问题不是“它是不是朋友”,而是“它是否具有私有访问权限”(前者足够但不必要)。 - GManNickG
@GMan- 你说得完全正确。感谢你指出这一点! - templatetypedef
请参见DR 45 - Fred Nurk
显示剩余3条评论
5个回答

83

在自己提出几乎相同问题这里后,我想分享(显然)针对C++11的更新答案:

引用来自https://stackoverflow.com/a/14759027/1984137

标准$11.7.1

“内嵌类是成员,因此具有与任何其他成员相同的访问权。封闭类的成员没有特殊访问嵌套类的成员的权限;应遵守通常的访问规则”。

通常的访问规则指定:

“类的成员还可以访问类拥有的所有名称……”

标准中给出了具体的例子:

class E {
    int x;
    class B { };

    class I {
        B b; // OK: E::I can access E::B
        int y;
        void f(E* p, int i) {
            p->x = i; // OK: E::I can access E::x
        }
    };
}

2
完全同意。我认为,这个问题的答案应该被修订,因为现在问题中呈现的代码可以正确编译。这是证明:链接 - Givi
gcc 4.8.2 存在一个错误,在 gcc 4.9.0 中才得以修复。 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59482 - Paulo Neves
简而言之:对于C++11及其以后的版本,是可以的,但对于C++03及其以前的版本,则不行。 - Daniel Kiss

54

直到C++11(即C++98和C++03):不行。

在C++98和C++03中,嵌套类默认情况下无法访问其包含类的privateprotected成员。

C++标准(2003年)在$11.8/1 [class.access.nest]中规定:

嵌套类的成员对包含类的成员以及授权给包含类的类或函数没有特殊访问权;应遵守通常的访问规则(第11条)。包含类的成员无法特别访问嵌套类的成员;应遵守通常的访问规则(第11条)。

来自标准本身的示例:

class E 
{
    int x;
    class B { };
    class I 
    {
        B b; // error: E::B is private
        int y;
        void f(E* p, int i)
        {
           p->x = i; // error: E::x is private
        }
   };
   int g(I* p)
   {
       return p->y; // error: I::y is private
   }
};

自C++11以来:是的。

自C++11以来,上述限制已被取消。现在,嵌套类可以访问封闭类的privateprotected成员:

class E 
{
    int x;
    class B { };
    class I 
    {
        B b; // ok: even though E::B is private
        int y;
        void f(E* p, int i)
        {
           p->x = i; // ok: even though E::x is private
        }
   };
   int g(I* p)
   {
       return p->y; // ok: even though I::y is private
   }
};

希望这有所帮助。

@templatetypedef:我知道嵌套类无法访问封闭类的私有成员,但引用了错误的参考。不管怎样,我已经更正了参考! - Nawaz
2
移除g()后,该代码在使用C++11及以上编译器时可以正常编译。建议更新答案。 - iammilind
如果您更新答案并提供有关C++11的相关信息,那将非常好,因为这里存在一个关键差异 ;) - Alexey
@Alexey:已更新。 - Nawaz
1
我尝试使用默认编译器(LLVM 9.0)在xcode 9.2上编译此代码,但编译失败。 它在return p->y一行处出现错误。 看来访问父类的私有成员是可以的,但从父类访问子类的私有成员是不可以的。 - Rafael Sabino

16

标准似乎已经改变了关于可访问性的规定。

C++98中的§11.8/1规定如下:

嵌套类的成员没有特殊访问权,不能访问封闭类的成员,也不能访问授予封闭类友元的类或函数;应遵守通常的访问规则。

N1804(TR1之后)中的§11.8/1规定如下:

嵌套类是一个成员,因此具有与任何其他成员相同的访问权限。

我认为当前的C++编译器遵守较新的规范。


2
嵌套类是一个成员,但是嵌套类的成员是否可以被视为封闭类的成员呢?这并不明显。嵌套类本身(而不是它的成员)可以像这样访问封闭类的成员:class Enclosing {private: enum PrivateEnum {VALUE}; class Nested {/*accessing enclosing class' member type*/ PrivateEnum e;}; }; - Sergei Tachenov
1
@SergeyTachenov:你好。 N1804中的§11.8/1也指出: “封闭类的成员对嵌套类的成员没有特殊访问权限;必须遵守通常的访问规则。” 这个声明从C++98开始就没有改变过。 因此,对于从封闭类到嵌套类的访问(与templatetypedef的问题相反),应遵守通常的访问规则。 - Ise Wisteria
@Ise,我不是在谈论如何访问嵌套类的成员,这是完全不同的问题。我只是不确定“嵌套类是一个成员”的概念是否也适用于嵌套类的成员。那么如果有几个层次的嵌套呢? - Sergei Tachenov
@SergeyTachenov:抱歉,我误解了你的评论。如果你的意思是问是否允许在标准中使用class A { int i; class B { struct C { void f( A* x ) { x->i = 0; } }; }; };,我认为允许它是标准的意图。VC8、g++3.4.5和Comeau online都允许它。但我不能确定。如果你有疑虑,我建议你在StackOverflow上发布这个问题。比我更详细了解的人会回答你的问题。 - Ise Wisteria
@Ise,抱歉我发了一些无关的东西。最初我是在评论N1804的引用:“嵌套类是一个成员” - 这是否意味着嵌套类的成员也是封闭类的成员?我的意思是,在class A {int i; class B {A *a; int f() {return a->i;} }; };中,类B是类A的成员,但B :: f()是类A :: B的成员,而不是A!但我刚刚发现DR 45的另一个引用可能会澄清这一点:“类的成员还可以访问其作为成员的类的所有名称。”换句话说,B :: f()“继承”了类B的访问权限。 - Sergei Tachenov
@SergeyTachenov: 我认为你的推断是正确的。 嵌套类和封闭类之间的关系可能是可传递的。 class A {int i; class B {A *a; int f() {return a->i;} }; }; VC8、g++3.4.5、ideone(gcc4.3.4)和Comeau online都接受你上面的代码 :-) - Ise Wisteria

4

这个答案与已过时的C++03规范相关。这个问题的被接受的答案更为实用。

现在我觉得自己很傻,因为我刚刚找到了涵盖此内容的规范部分:§11.8/1:

嵌套类的成员对于外围类的成员,以及授予外围类友元的类或函数没有特殊访问权限;应遵守通常的访问规则(第11条)。 外围类的成员对于嵌套类的成员没有特殊访问权限;应遵守通常的访问规则(第11条)。

(我加粗了)

所以看起来,内部类没有特殊的访问权限。


你误解了那段话。没有11.8.1这个版本号,你是不是指的是11.8p1(或者是“11.8/1”)? - Fred Nurk
@Fred Nurk- 哎呀,我是说11.8/1。我会修正的。 - templatetypedef
@Fred Nurk- 另外,当你说我误解了它时,是指我解释错误了,还是只是错标了它? - templatetypedef
@Fred:templatetypedef是正确的。请看我的帖子。我还引用了标准本身的例子。 - Nawaz

3

我不记得头脑中的确切位置,但我记得阅读规格并发现类中的任何私有数据都对所有其他类(包括嵌套类)隐藏。

基本上,嵌套类定义了某个范围,而不是访问权限。


1
你是错误的,但我只是-1,因为即使正确答案在附近被引用,这个回答还是得到了+1。@投票者,请在投票之前阅读所有答案。 - GManNickG

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