私有继承的 typedef 对嵌套类的可见性

发布于 2024-08-24 20:34:26 字数 1650 浏览 5 评论 0 原文

在下面的示例中(对长度表示歉意),我尝试隔离在私有继承自另一个类的类中使用嵌套类时遇到的一些意外行为。我经常看到这样的说法:与非嵌套类相比,嵌套类没有什么特别之处,但在这个例子中,我们可以看到嵌套类(至少根据 GCC 4.4)可以看到 a 的公共 typedef。由关闭类私有继承的类。

我知道 typedef 与成员数据不同,但我发现这种行为令人惊讶,我想许多其他人也会如此。所以我的问题是双重的:

  1. 这是标准行为吗? (对为什么的一个体面的解释会非常有帮助)
  2. 人们可以期望它在大多数现代编译器上工作吗(即,它的可移植性如何)?

#include <iostream>

class Base {
  typedef int priv_t;
  priv_t priv;
public:
  typedef int pub_t;
  pub_t pub;
  Base() : priv(0), pub(1) {}
};

class PubDerived : public Base {
public:
  // Not allowed since Base::priv is private
  // void foo() {std::cout << priv << "\n";}

  class Nested {
    // Not allowed since Nested has no access to PubDerived member data
    // void foo() {std::cout << pub << "\n";}

    // Not allowed since typedef Base::priv_t is private
    // void bar() {priv_t x=0; std::cout << x << "\n";}
  };

};

class PrivDerived : private Base {
public:
  // Allowed since Base::pub is public
  void foo() {std::cout << pub << "\n";}

  class Nested {
  public:
    // Works (gcc 4.4 - see below)
    void fred() {pub_t x=0; std::cout << x << "\n";}
  };
};

int main() {

  // Not allowed since typedef Base::priv_t private
  // std::cout << PubDerived::priv_t(0) << "\n";

  // Allowed since typedef Base::pub_t is inaccessible
  std::cout << PubDerived::pub_t(0) << "\n"; // Prints 0

  // Not allowed since typedef Base::pub_t is inaccessible
  //std::cout << PrivDerived::pub_t(0) << "\n";

  // Works (gcc 4.4)
  PrivDerived::Nested o;
  o.fred(); // Prints 0
  return 0;
}

In the following example (apologies for the length) I have tried to isolate some unexpected behaviour I've encountered when using nested classes within a class that privately inherits from another. I've often seen statements to the effect that there is nothing special about a nested class compared to an unnested class, but in this example one can see that a nested class (at least according to GCC 4.4) can see the public typedefs of a class that is privately inherited by the closing class.

I appreciate that typdefs are not the same as member data, but I found this behaviour surprising, and I imagine many others would, too. So my question is twofold:

  1. Is this standard behaviour? (a decent explanation of why would be very helpful)
  2. Can one expect it to work on most modern compilers (i.e., how portable is it)?

#include <iostream>

class Base {
  typedef int priv_t;
  priv_t priv;
public:
  typedef int pub_t;
  pub_t pub;
  Base() : priv(0), pub(1) {}
};

class PubDerived : public Base {
public:
  // Not allowed since Base::priv is private
  // void foo() {std::cout << priv << "\n";}

  class Nested {
    // Not allowed since Nested has no access to PubDerived member data
    // void foo() {std::cout << pub << "\n";}

    // Not allowed since typedef Base::priv_t is private
    // void bar() {priv_t x=0; std::cout << x << "\n";}
  };

};

class PrivDerived : private Base {
public:
  // Allowed since Base::pub is public
  void foo() {std::cout << pub << "\n";}

  class Nested {
  public:
    // Works (gcc 4.4 - see below)
    void fred() {pub_t x=0; std::cout << x << "\n";}
  };
};

int main() {

  // Not allowed since typedef Base::priv_t private
  // std::cout << PubDerived::priv_t(0) << "\n";

  // Allowed since typedef Base::pub_t is inaccessible
  std::cout << PubDerived::pub_t(0) << "\n"; // Prints 0

  // Not allowed since typedef Base::pub_t is inaccessible
  //std::cout << PrivDerived::pub_t(0) << "\n";

  // Works (gcc 4.4)
  PrivDerived::Nested o;
  o.fred(); // Prints 0
  return 0;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

够运 2024-08-31 20:34:26

前言:在下面的答案中,我提到了 C++98 和 C++03 之间的一些差异。然而,事实证明我正在谈论的更改尚未成为标准,因此 C++03 在这方面与 C++98 并没有真正的不同(感谢 Johannes 指出这一点)。不知怎的,我确信我在 C++03 中看到过它,但实际上它不在那里。然而,这个问题确实存在(请参阅 Johannes 评论中的 DR 参考),并且一些编译器已经实现了他们可能认为是该问题最合理的解决方案。因此,下面的文本中对 C++03 的引用是不正确的。请将对 C++03 的引用解释为对此行为的某些假设但很可能是未来规范的引用,一些编译器已经在尝试实现该规范。


值得注意的是,C++98 和 C++03 标准之间的嵌套类访问权限发生了重大变化。

在 C++98 中,嵌套类对封闭类的成员没有特殊的访问权限。它基本上是完全独立的类,只是在封闭类的范围内声明。它只能访问封闭类的公共成员。

在 C++03 中,嵌套类被授予对封闭类成员的访问权限作为封闭类的成员。更准确地说,嵌套类被赋予了与封闭类的静态成员函数相同的访问权限。即,现在嵌套类可以访问封闭类的任何成员,包括私有成员。

因此,您可能会观察到不同编译器和同一编译器版本之间的差异,具体取决于它们实现新规范的时间。

当然,您必须记住,嵌套类的对象不以任何方式与封闭类的任何特定对象绑定。就实际对象而言,这是两个独立的类。为了从嵌套类访问封闭类的非静态数据成员或方法,您必须拥有封闭类的特定对象。换句话说,嵌套类的行为确实就像封闭类的静态成员函数一样:它没有封闭类的特定 this 指针,因此它无法访问封闭类的非静态成员,除非您努力为其提供封闭类的特定对象来访问。如果没有它,嵌套类只能访问封闭类的 typedef 名称、枚举和静态成员。

说明 C++98 和 C++03 之间差异的简单示例可能如下所示。

class E {
  enum Foo { A };
public:
  enum Bar { B };

  class I {
    Foo i; // OK in C++03, error in C++98
    Bar j; // OK in C++03, OK in C++98
  };
};

此更改正是允许您的 PrivDerived::Nested::fred 函数进行编译的原因。它无法通过迂腐的 C++98 编译器的编译。

Preface: In the answer below I refer to some differences between C++98 and C++03. However, it turns out that the change I'm talking about haven't made it into the standard yet, so C++03 is not really different from C++98 in that respect (thanks to Johannes for pointing that out). Somehow I was sure I saw it in C++03, but in reality it isn't there. Yet, the issue does indeed exist (see the DR reference in Johannes comment) and some compilers already implement what they probably consider the most reasonable resolution of that issue. So, the references to C++03 in the text below are not correct. Please, interpret the references to C++03 as references to some hypothetical but very likely future specification of this behavior, which some compilers are already trying to implement.


It is important to note that there was a significant change in access rights for nested classes between C++98 and C++03 standards.

In C++98 nested class had no special access rights to the members of enclosing class. It was basically completely independent class, just declared in the scope of the enclosed class. It could only access public members of the enclosing class.

In C++03 nested class was given access rights to the members of the enclosing class as a member of the enclosing class. More precisely, nested class was given the same access rights as a static member function of the enclosing class. I.e. now the nested class can access any members of the enclosing class, including private ones.

For this reason, you might observe the differences between different compilers and versions of the same compiler depending on when they implemented the new specification.

Of course, you have to remember that an object of the nested class is not tied in any way to any specific object of the enclosing class. As far as the actual objects are concerned, these are two independent classes. In order to access the non-static data members or methods of the enclosing class from the nested class you have to have a specific object of the enclosing class. In other words, once again, the nested class indeed behaves as just like a static member function of the enclosing class: it has no specific this pointer for the enclosing class, so it can't access the non-static members of the enclosing class, unless you make an effort to give it a specific object of the enclosing class to access. Without it the nested class can only access typedef-names, enums, and static members of the enclosing class.

A simple example that illustrates the difference between C++98 and C++03 might look as follows

class E {
  enum Foo { A };
public:
  enum Bar { B };

  class I {
    Foo i; // OK in C++03, error in C++98
    Bar j; // OK in C++03, OK in C++98
  };
};

This change is exactly what allows your PrivDerived::Nested::fred function to compile. It wouldn't pass compilation in a pedantic C++98 compiler.

长不大的小祸害 2024-08-31 20:34:26

简短回答:嵌套类可以访问 C++0x 中包含类的私有成员,但不能访问 C++1998 和 C++2003。然而,对于支持 C++0x 行为的 C++98 和 C++2003 编译器来说,这是合法的,因为旧的行为被视为缺陷。

在 C++98 和 2003 标准第 11.8.1 节中指出:

嵌套类的成员没有
对某组织成员的特殊访问权
封闭类,也不是类或
赋予友谊的功能
封闭类;平常的
访问规则(第 11 条)应
服从了。封闭的成员
类没有特殊访问权限
嵌套类的成员;平常的
访问规则(第 11 条)应
服从了。

C++0x 第 11.8.1 节说:

嵌套类是一个成员,因此
与任何人具有相同的访问权限
其他成员。的成员
封闭类没有特殊访问权限
嵌套类的成员;这
通常的访问规则(第 11 条)应
必须服从。

核心语言缺陷报告 45 显示原始行为被认为是一个缺陷,因此非 c++0x 编译器也支持新行为是合法的,尽管不是必需的。

Short answer: Nested classes have access to the containing classes private member in C++0x, but not C++1998 and C++2003. It is however legal for C++98 and C++2003 compilers support the C++0x behavior, since the old behavior is considered a defect.

In the C++98 and 2003 standards section 11.8.1 stated:

The members of a nested class have no
special access to members of an
enclosing class, nor to classes or
functions that have granted friendship
to an enclosing class; the usual
access rules (clause 11) shall be
obeyed. The members of an enclosing
class have no special access to
members of a nested class; the usual
access rules (clause 11) shall be
obeyed.

C++0x section 11.8.1 says:

A nested class is a member and as such
has the same access rights as any
other member. The members of an
enclosing class have no special access
to members of a nested class; the
usual access rules (Clause 11) shall
be obeyed.

Core Language Defect Report 45 shows that the original behavior was considered a defect, so it is legal for non-c++0x compilers to also support the new behavior, although not required.

池予 2024-08-31 20:34:26

根据标准:

9.2 班级成员

1 [...] 类的成员包括数据成员、成员函数 (9.3)、嵌套类型、
和枚举器。数据成员和成员函数是静态的或非静态的;参见 9.4。嵌套类型是
类中定义的类(9.1、9.7)和枚举(7.2),以及使用声明为成员的任意类型
typedef 声明 (7.1.3)。

回答您的问题:

  1. 这是标准行为吗? (一个合理的解释为什么会是
    非常有帮助)

不。至少在 typedef 无法访问的情况下。但请注意:

class Nested {
    // Not allowed since Nested has no access to PubDerived member data
     void foo() {std::cout << pub << "\n";}

是有问题的。嵌套类既没有可使用的 PubDerived 实例,而且 pub 也不是 static 成员对象。

  • 人们能否期望它能够在大多数现代编译器上工作(即,如何
    是便携式的吗)?
  • 是的。但请检查文档是否符合标准。始终:在严格模式下尝试一些编译器,例如 Comeau。

    As per the standard:

    9.2 Class members

    1 [...] Members of a class are data members, member functions (9.3), nested types,
    and enumerators. Data members and member functions are static or non-static; see 9.4.Nested types are
    classes (9.1, 9.7) and enumerations (7.2) defined in the class, and arbitrary types declared as members by use
    of a typedef declaration (7.1.3).

    To answer your questions:

    1. Is this standard behaviour? (a decent explanation of why would be
      very helpful)

    No. At least with the typedefs not being accessible. However, note that:

    class Nested {
        // Not allowed since Nested has no access to PubDerived member data
         void foo() {std::cout << pub << "\n";}
    

    is problematic. The nested class neither has an instance of PubDerived to work with nor is the pub a static member object.

    1. Can one expect it to work on most modern compilers (i.e., how
      portable is it)?

    Yes. But do check the documentation for standards compliance. And always: Try out with a few compilers such as Comeau in strict mode.

    酒绊 2024-08-31 20:34:26

    我已尽力收集 ISO/IEC 14882:1997 中的所有相关条款。

    第 9.7 节:

    在另一个类中定义的类称为嵌套类。嵌套类的名称是其封闭类的本地名称。嵌套类位于其封闭类的范围内。除了使用显式指针、引用和对象名称之外,嵌套类中的声明只能使用封闭类中的类型名称、静态成员和枚举器。

    11.2.1(应该相当明显):

    [...] 如果使用 private 访问说明符将一个类声明为另一个类的基类,则基类的公共和受保护成员可以作为派生类的私有成员进行访问。

    9.9 嵌套类型名称:

    类型名称遵循与其他名称完全相同的范围规则。

    然后在 11.8 中:

    嵌套类的成员没有对封闭类的成员的特殊访问权限,也没有对封闭类授予友谊的类或函数的特殊访问权限;应遵守通常的访问规则(11)。封闭类的成员对嵌套类的成员没有特殊访问权限;应遵守通常的访问规则(11)。

    由此我得出的结论是,您所经历的行为是不标准的。嵌套类不应该对基类的私有成员有任何“特殊”访问权限。

    然而,似乎拥有最好标准支持的 Comeau C++ 具有与 GCC 相同的行为(允许 fred,不允许 bar 带有“error: type”Base::priv_t “(在第 4 行声明)无法访问”)。

    I've done my best to assemble all the relevant clauses from the ISO/IEC 14882:1997.

    Section 9.7:

    A class defined within another is called a nested class. The name of a nested class is local to its enclosing class. The nested class is in the scope of its enclosing class. Except by using explicit pointers, references, and object names, declarations in a nested class can use only type names, static members, and enumerators from the enclosing class.

    11.2.1 (should be fairly obvious):

    [...] If a class is declared to be a base class for another class using the private access specifier, the public and protected members of the base class are accessible as private members of the derived class.

    9.9 Nested Type Names:

    Type names obey exactly the same scope rules as other names.

    Then in 11.8:

    The members of a nested class have no special access to members of an enclosing class, nor to classes or functions that have granted friendship to an enclosing class; the usual access rules (11) shall be obeyed. The members of an enclosing class have no special access to members of a nested class; the usual access rules (11) shall be obeyed.

    From which I conclude that the behaviour you're experiencing is non-standard. The nested class shouldn't have any 'special' access to the private member of the base.

    However, Comeau C++, which seems to have the best standard support, has the same behaviour as GCC (allows fred, disallows bar with "error: type "Base::priv_t" (declared at line 4) is inaccessible").

    玩套路吗 2024-08-31 20:34:26

    这并没有回答你的问题,但根据我对 C++ FAQ Lite 24.6 您尝试执行的操作是不允许的。我不确定为什么海湾合作委员会允许这样做;你在其他编译器中也尝试过吗?

    This doesn't answer your question, but according to my reading of the C++ FAQ Lite 24.6 what you are trying to do isn't allowed. I'm not sure why gcc is permitting it; have you tried it in other compilers as well?

    ~没有更多了~
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文