代码之家  ›  专栏  ›  技术社区  ›  beldaz Nicolas W.

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

  •  8
  • beldaz Nicolas W.  · 技术社区  · 15 年前

    在下面的示例中(对长度表示歉意),我试图隔离在一个私有继承自另一个类的类中使用嵌套类时遇到的一些意外行为。我经常看到这样的语句,即嵌套类与未被列出的类相比没有什么特别之处,但是在本例中,我们可以看到嵌套类(至少根据gcc 4.4)可以看到由关闭类私有继承的类的公共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;
    }
    
    5 回复  |  直到 12 年前
        1
  •  4
  •   AnT stands with Russia    15 年前

    前言: 在下面的答案中,我提到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编译器进行编译。

        2
  •  2
  •   catphive    15 年前

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

    在C++ 98和2003标准中,第11章1节指出:

    嵌套类的成员没有 特殊访问 封闭类,也不属于类或 赋予友谊的功能 封闭类;通常 访问规则(第11条)应 服从。封闭的成员 类没有特殊的访问权限 嵌套类的成员;通常 访问规则(第11条)应 服从。

    C++0X部分11.

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

    Core Language Defect Report 45 显示原始行为被视为缺陷,因此非C++0x编译器也支持新行为是合法的,尽管这不是必需的。

        3
  •  1
  •   dirkgently    15 年前

    按照标准:

    9.2班级成员

    类的成员是数据成员、成员函数(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 成员对象。

    1. 人们能指望它在大多数现代编译器上工作吗(例如 是便携式的吗?

    对。但是一定要检查文档是否符合标准。并且总是:在严格模式下使用一些编译器(如comeau)进行尝试。

        4
  •  1
  •   p00ya    15 年前

    我已经尽了最大努力将ISO/IEC 14882:1997中的所有相关条款汇编起来。

    第9.7节:

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

    11.2.1(应相当明显):

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

    9.9嵌套类型名:

    类型名与其他名称遵循完全相同的作用域规则。

    然后在11.8中:

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

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

    然而,COMUC+,它似乎有最好的标准支持,具有与GCC相同的行为(允许)。 fred ,不允许 bar “错误:类型”base::priv_t“(在第4行声明)不可访问”)。

        5
  •  0
  •   fbrereto    15 年前

    这不回答你的问题,但根据我读到的 the C++ FAQ Lite 24.6 你想做的事是不允许的。我不知道GCC为什么允许它;您在其他编译器中也尝试过它吗?