下面的第一个片段编译,但第二个没有。为什么?

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 不会改变任何内容,因为 Amain 中的声明会影响另一个 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-seqopt 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 应该是什么(全局定义被局部原型遮蔽,导致它被忽略)。