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。可能是编译器错误与标准相矛盾吗?
答案 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)
我认为,如果将声明和定义(转换为标题和实现文件)分开,则保留类中的自由排序的承诺。一旦你拆分它们,订单就不再那么重要了。