下面的第一个片段编译,但第二个没有。为什么?
The first snippet below compiles, but the second doesn't. Why?
下面的代码片段编译 (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 and [basic.scope.pdecl]/7.
但我真的不知道如何从这两段中推断出上面显示的不同行为。
请注意,在第一个示例中 struct A
而不是 首先在 elaborated-type-specifier struct A;
,但是在struct A
的定义中main()
.
在第二个示例中,struct A
也 不是 首先在 elaborated-type-specifier struct A;
,但在全局范围内定义struct A
。
在第二个示例中,行 struct A;
是主函数范围内名为 A 的结构的前向声明。此结构将优于全局 struct A
。下一行定义了一个类型为 struct A
的名为 a
的变量。由于 struct A
是在 main 函数的范围内声明的,因此编译器将在那里搜索它的定义。它找不到一个(它被注释掉了)。第一个示例编译是因为在同一范围内有定义。但是,以下示例将编译,因为它指定 A
在全局命名空间中:
struct A{ int i = 10; };
int main() {
// struct A{ int i = 20; };
struct A;
struct ::A a;
}
每个示例都包含两个不同的 classes 的声明,它们的名称都为 A
.
让我们通过将其中一个重命名为 B
:
来区分 classes
struct A{ int i = 10; };
int main() {
struct B{ int i = 20; };
struct B;
struct B b;
}
以上在语义上与您的第一个示例相同。 class A
从未使用过。
struct A{ int i = 10; };
int main() {
struct B;
struct B b;
}
这在语义上与您的第二个示例相同。您正在尝试创建一个不完整类型的对象,前向声明的 class B
.
将 B
重命名回 A
不会改变任何内容,因为 A
在 main
中的声明会影响另一个 A
的声明在全球范围内。
[basic.lookup.elab]/2
If the elaborated-type-specifier has no nested-name-specifier, and [...] if the elaborated-type-specifier appears in a declaration with the form:
class-key
attribute-specifier-seq
opt identifier
;
elaborated-type-specifier 是一个引入 class-name 的声明,如 [basic.scope.pdecl].
所以struct A;
是一个声明,引入声明范围内的class名称。在任何情况下都不能引用在外部作用域中声明的 class。
[basic.scope.pdecl]/7
[ Note: Other forms of elaborated-type-specifier do not declare a new name [...] — end note ]
暗示,这种形式的 elaborated-type-specifier 声明了一个新名称。
它没有编译,因为它找不到 A 的定义。
int main() {
// struct A{ int i = 20; };
struct A;
struct A a;
}
上面的代码等同于您的第一个示例,因为全局 A 被局部 A 覆盖。在第二个示例中,A 没有定义。这只是一个原型。当定义放在需要它的代码之后时,原型应该放在需要定义的一段代码之前。
如果编译器找不到该定义,它将失败,因为它不知道 A 应该是什么(全局定义被局部原型遮蔽,导致它被忽略)。
下面的代码片段编译 (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 and [basic.scope.pdecl]/7.
但我真的不知道如何从这两段中推断出上面显示的不同行为。
请注意,在第一个示例中 struct A
而不是 首先在 elaborated-type-specifier struct A;
,但是在struct A
的定义中main()
.
在第二个示例中,struct A
也 不是 首先在 elaborated-type-specifier struct A;
,但在全局范围内定义struct A
。
在第二个示例中,行 struct A;
是主函数范围内名为 A 的结构的前向声明。此结构将优于全局 struct A
。下一行定义了一个类型为 struct A
的名为 a
的变量。由于 struct A
是在 main 函数的范围内声明的,因此编译器将在那里搜索它的定义。它找不到一个(它被注释掉了)。第一个示例编译是因为在同一范围内有定义。但是,以下示例将编译,因为它指定 A
在全局命名空间中:
struct A{ int i = 10; };
int main() {
// struct A{ int i = 20; };
struct A;
struct ::A a;
}
每个示例都包含两个不同的 classes 的声明,它们的名称都为 A
.
让我们通过将其中一个重命名为 B
:
struct A{ int i = 10; };
int main() {
struct B{ int i = 20; };
struct B;
struct B b;
}
以上在语义上与您的第一个示例相同。 class A
从未使用过。
struct A{ int i = 10; };
int main() {
struct B;
struct B b;
}
这在语义上与您的第二个示例相同。您正在尝试创建一个不完整类型的对象,前向声明的 class B
.
将 B
重命名回 A
不会改变任何内容,因为 A
在 main
中的声明会影响另一个 A
的声明在全球范围内。
[basic.lookup.elab]/2
If the elaborated-type-specifier has no nested-name-specifier, and [...] if the elaborated-type-specifier appears in a declaration with the form:
class-key
attribute-specifier-seq
optidentifier
;
elaborated-type-specifier 是一个引入 class-name 的声明,如 [basic.scope.pdecl].
所以struct A;
是一个声明,引入声明范围内的class名称。在任何情况下都不能引用在外部作用域中声明的 class。
[basic.scope.pdecl]/7
[ Note: Other forms of elaborated-type-specifier do not declare a new name [...] — end note ]
暗示,这种形式的 elaborated-type-specifier 声明了一个新名称。
它没有编译,因为它找不到 A 的定义。
int main() {
// struct A{ int i = 20; };
struct A;
struct A a;
}
上面的代码等同于您的第一个示例,因为全局 A 被局部 A 覆盖。在第二个示例中,A 没有定义。这只是一个原型。当定义放在需要它的代码之后时,原型应该放在需要定义的一段代码之前。 如果编译器找不到该定义,它将失败,因为它不知道 A 应该是什么(全局定义被局部原型遮蔽,导致它被忽略)。