c ++课堂上的defs顺序,令人惊讶

时间:2011-06-08 15:11:10

标签: c++

Stroustrup在C ++语言书中指出,班级中的定义顺序无关紧要 事实上:

class C1 { 
       int foo() { return bar(); }    // where is bar() ?
       int bar() { return m_count; }  // oh, here is bar(). but where is m_count ?
       int m_count;                   // here is m_count. Better late than never !
}

这个编译。尽管有错误。按照承诺。 到目前为止,非常好。

但是,这不会编译:

class C2 {
      void baz(Inner *p) {} // we were promised that order does not matter
                            // is Inner defined ?
      struct Inner {};      // yes, Inner is define here.
};

这看起来与Stroustrup在课堂上自由订购的承诺相矛盾。引用Stroustrup:“在类中声明的成员函数可以引用类的每个成员,就好像在考虑成员函数体之前完全定义了类一样。”

有人知道ref允许标准条款允许C1并禁止C2吗?我很好奇为什么在允许C1时不允许使用C2。可能是编译器错误与标准相矛盾吗?

5 个答案:

答案 0 :(得分:5)

请注意,以下编译正常(在VS2008中):

class C2 {
  void baz() {  Inner* i = new Inner(); }
  struct Inner {}; 
};

您的两个示例之间存在两个差异。第一个在函数的 body 中使用未声明的符号,第二个在签名中使用未声明的类型功能。

我怀疑我的示例和您的第一个示例都有效,因为直到解析完整个类声明之后才解析函数体。功能签名一旦遇到就必须有意义。

答案 1 :(得分:5)

表示类型的标识符必须在使用前声明。这与C ++语法的复杂结构有关。如果事先不知道哪些标识符是类型,编译器就无法解析某些代码。

答案 2 :(得分:3)

您将“声明”与“定义”混淆。内部类必须声明 - 如果只是前向声明 - 才可以使用它。

答案 3 :(得分:2)

定义的顺序无关紧要,但声明的顺序确实如此。

在C ++中,声明(粗略地)表示符号的“种类”或类型:

  • class A; - > A是善良的“阶级”
  • void foo(int); - > foo是一个接受int并且不返回任何内容的函数

然而,定义完全定义了符号的含义。与在C中一样,定义恰好也是一个声明。

最后,为了进一步混淆水域,对于用户声明的类型,有时需要一个定义(完整类型),而有时一个简单的声明就足够了......

现在,让我们修改你的问题,我将用一个使用C ++ 0x的简单例子来说明它:

struct A {
  void foo() { ++count; }
  void bar(decltype(count) c); // error: 'count' was not declared in this scope

  int count;
};

struct B {
  void foo() { Inner a; a.foo(); }
  void bar(Inner& i); // error: 'Inner' has not been declared

  struct Inner { void foo(); };
};

您可以注意到,A::foo已正确解析,而A::bar则未正确解析。

问题来自编译器“欺骗”的事实:只有在完全解析了类之后,才会对方法体进行全面分析。因此,在函数体中引用尚未声明的类型/属性/函数是可以的,但在函数签名(声明)中引用它是不正确的。

您可以对编译器说,代码等同于:

struct A {
  void foo();
  void bar(decltype(count) c); // error: 'count' was not declared in this scope

  int count;
};
void A::foo() { ++count; }

struct B {
  void foo();
  void bar(Inner& i); // error: 'Inner' has not been declared

  struct Inner { void foo(); };
};
void B::foo() { Inner a; a.foo(); }

Stroustrup句子“容易”,但不一定准确。例如,使用Inner作为属性要求它是完全定义的(只有完整类型可以用作非静态属性),这可能会导致进一步的悲伤。

struct C {
  struct Inner;

  Inner foo; // error: field 'foo' has incomplete type

  struct Inner { };
};

虽然有人可能会说Inner foo是一个声明,因此没有被Stroustrup引用所涵盖。

答案 4 :(得分:0)

我认为,如果将声明和定义(转换为标题和实现文件)分开,则保留类中的自由排序的承诺。一旦你拆分它们,订单就不再那么重要了。