8

在下面的示例中(为篇幅道歉),我试图隔离在从另一个私有继承的类中使用嵌套类时遇到的一些意外行为。我经常看到这样的声明,即嵌套类与非嵌套类相比没有什么特别之处,但在这个例子中,我们可以看到嵌套类(至少根据 GCC 4.4)可以看到一个公共类型定义由关闭类私有继承的类。

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

  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;
}
4

5 回答 5

4

前言:在下面的答案中,我提到了 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封闭类的指针,所以它不能访问封闭类的非静态成员,除非你努力给它一个封闭类的特定对象来访问。没有它,嵌套类只能访问封闭类的类型定义名称、枚举和静态成员。

说明 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 编译器中通过编译。

于 2010-03-11T00:13:35.370 回答
2

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

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

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

C++0x 第 11.8.1 节说:

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

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.

于 2010-12-24T20:09:44.693 回答
1

按照标准:

9.2 班级成员

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

要回答您的问题:

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

不,至少typedefs 不可访问。但是,请注意:

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

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

  1. 可以期望它在大多数现代编译器上工作(即,它的可移植性如何)?

是的。但请务必检查文档是否符合标准。并且总是:在严格模式下尝试使用一些编译器,例如 Comeau。

于 2010-03-10T23:53:23.853 回答
1

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

第 9.7 节:

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

11.2.1(应该相当明显):

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

9.9 嵌套类型名称:

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

然后在 11.8 中:

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

我从中得出结论,您遇到的行为是非标准的。嵌套类不应该对基的私有成员有任何“特殊”访问权限。

但是,似乎拥有最佳标准支持的 Comeau C++ 具有与 GCC 相同的行为(允许fred,禁止bar使用“错误:类型“Base::priv_t”(在第 4 行声明)不可访问”)。

于 2010-03-10T23:55:02.450 回答
0

这不能回答您的问题,但根据我对 C++ FAQ Lite 24.6 的阅读,您正在尝试做的事情是不允许的。我不确定为什么 gcc 允许它;您是否也在其他编译器中尝试过?

于 2010-03-10T23:54:56.503 回答