下面的代码段编译(demo):
struct A{ int i = 10; };
int main() {
struct A{ int i = 20; };
struct A;
struct A a;
}
但这并不是:
struct A{ int i = 10; };
int main() {
// struct A{ int i = 20; };
struct A;
struct A a;
}
我可以看到答案可能是标准中的这些段落:
[basic.lookup.elab]/2和[basic.scope.pdecl]/7。
但我真的不知道如何从这两段中推断出上述不同的行为。
请注意,在第一个示例中,struct A
首先在 elaborated-type-specifier struct A;
中声明 ,但在定义中struct A
中的main()
。
在第二个示例中,struct A
在 elaborated-type-specifier struct A;
中首次声明 ,但在定义中struct A
在全球范围内。
答案 0 :(得分:67)
每个示例都包含两个不同类的声明,两个名称都为A
。
让我们通过将其中一个重命名为B
来区分类:
struct A{ int i = 10; };
int main() {
struct B{ int i = 20; };
struct B;
struct B b;
}
以上内容与您的第一个示例在语义上相同。从未使用过类A
。
struct A{ int i = 10; };
int main() {
struct B;
struct B b;
}
这在语义上与第二个示例相同。您正在尝试创建一个不完整类型的对象,即前向声明的类B
。
将B
重命名为A
不会改变任何内容,因为A
中main
的声明会影响全球另一个A
的声明范围。
<强> [basic.lookup.elab] / 2 强>
如果 elaborated-type-specifier 没有嵌套名称说明符,并且[...]如果 elaborated-type-specifier 出现在声明中,格式为:
elaborated-type-specifier 是一个声明,它引入了 class-name ,如[basic.scope.pdecl]中所述。
class-key
attribute-specifier-seq
optidentifier
{{1 }}
因此;
是声明,在声明范围内引入类名称。在任何情况下都不能引用在外部范围内声明的类。
<强> [basic.scope.pdecl] / 7 强>
[注意:其他形式的 elaborated-type-specifier 不会声明新名称[...] - 结束注释]
暗示,这种形式的 elaborated-type-specifier 声明了一个新名称。
答案 1 :(得分:44)
在第二个示例中,行struct A;
是主函数范围内名为A的结构的前向声明。此结构将优先于全局struct A
。下一行定义了一个名为a
的{{1}}类型的变量。由于在main函数的作用域中声明了struct A
,因此编译器将在那里搜索它的定义。它没有找到一个(它被注释掉了)。第一个示例编译,因为在同一范围内有定义。但是,以下示例将编译,因为它指定struct A
位于全局命名空间中:
A
答案 2 :(得分:5)
它无法编译,因为它无法找到A的定义。
int main() {
// struct A{ int i = 20; };
struct A;
struct A a;
}
上面的代码等于你的第一个例子,因为全局A被本地A遮蔽。在第二个例子中,A没有定义。这只是一个原型。原型应该放在需要定义的代码之前,当定义放在需要它的代码之后。 如果编译器找不到该定义,它将失败,因为它不知道A应该是什么(全局定义被本地原型遮蔽,导致它被忽略)。