【问题标题】:The first snippet below compiles, but the second doesn't. Why?下面的第一个片段编译,但第二个没有。为什么?
【发布时间】:2017-05-19 04:39:07
【问题描述】:

下面的sn-p编译(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 的定义中在全球范围内。

【问题讨论】:

  • 为什么language-lawyer 标签被删除了?
  • 我想了解为什么@RyanHaining 首先将其删除。也许他是有原因的。
  • 当然有一些灰色地带。我在 C++ 语言律师问题中看到的总体趋势是编译器不同意,或者编译的程序没有保证它们是明确定义的行为。这些将是“语言律师”讨论哪个编译器行为正确,或者程序是否符合定义良好的标准要求的地方。由于您应该从此代码收到准确的错误消息,因此 IMO 它超出了通常与 [language-lawyer] 发布的内容
  • 实际上,这似乎不太适合 language-lawyer 标签。标准很明确,sn-p 格式不正确。就这样。我的两分钱。
  • @RyanHaining AFAIK language-lawyer 只是表示,OP 希望从官方标准中获得准确的参考,以澄清问题中发生的情况。因此,可以使用所述标签询问有关 任何 行为的问题,无论您是否觉得它们有趣。

标签: c++ language-lawyer declaration definition name-lookup


【解决方案1】:

每个示例都包含两个不同类的声明,均名为 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 不会改变任何内容,因为Amain 中的声明会影响另一个A 在全局范围内的声明。

[basic.lookup.elab]/2

如果 elaborated-type-specifier 没有 nested-name-specifier,并且 [...] 如果 elaborated-type-specifier em> 以如下形式出现在声明中:

class-key attribute-specifier-seqopt identifier ;

elaborated-type-specifier 是一个声明,它引入了 [basic.scope.pdecl] 中描述的 class-name

所以struct A; 是一个声明,它在声明的范围内引入类名。在任何情况下,它都不能引用在外部范围内声明的类。

[basic.scope.pdecl]/7

[注意:其他形式的详细类型说明符不声明新名称[...] - 结束说明]

暗示,这种形式的详细类型说明符声明了一个新名称。

【讨论】:

  • 1) [basic.scope.pdecl]/7 以以下语句开头:The point of declaration of a class **first** declared in an elaborated-type-specifier is as follows:。这意味着,只有在声明 class A;firstelaborated-type-specifier 中声明时才考虑项目符号点 (7.1) 和 (7.2),这不是正如我在问题末尾指出的那样,这两个示例中的情况都是如此。
  • 2) 所以,就 [basic.scope.pdecl]/7 而言,在我的第二个示例中,我认为我们不能说 main() 中的声明 struct A; , 不能引用在外部范围内声明的类,如您在上面断言的那样。无论如何感谢您的回复。
  • 我解释了为什么,在你的第二个例子中,struct A; A 的第一个声明,因为在全局范围内的A 是一个完全不同的类同名。
  • [basic.scope.declarative]/1:“声明的范围与其潜在范围相同,除非潜在范围包含另一个同名声明。在这种情况下,潜在范围内部声明区域中的声明被排除在外部声明区域中的声明范围之外。”
  • 我想我在[class.name]/2 中找到了我的问题的答案,它准确地说明了您在回答中所说的内容 (Under no circumstances can it refer to a class declared in an outer scope.)。谢谢 (+1)。
【解决方案2】:

在第二个示例中,struct A; 行是主函数范围内名为 A 的结构的前向声明。此结构将优先于全局struct A。下一行定义了一个名为a 的变量,其类型为struct A。由于struct A 是在主函数的作用域中声明的,因此编译器将在那里搜索它的定义。它找不到一个(已被注释掉)。第一个示例编译是因为在同一范围内有定义。但是,以下示例将编译,因为它指定 A 在全局命名空间中:

struct A{ int i = 10; };

int main() {
//    struct A{ int i = 20; };
    struct A;
    struct ::A a;
}

【讨论】:

  • 我会明确提到这是一个前向声明
  • @skypjack 谢谢。我会解决这个疏忽。
  • @FrançoisAndrieux:我不会将其描述为“疏忽”。您的原始版本在技术上是正确的(struct A; 是一个声明,但不是一个定义)。然而,写“前向声明”而不是“声明”确实让非语言律师更容易阅读答案。
【解决方案3】:

它无法编译,因为它找不到 A 的定义。

int main() {
//    struct A{ int i = 20; };
      struct A;
      struct A a;
}

上面的代码与您的第一个示例相同,因为全局 A 被本地 A 遮蔽。在第二个示例中,A 没有定义。这只是一个原型。当定义放在需要它的代码之后时,原型应该放在需要定义的一段代码之前。 如果编译器找不到该定义,它将失败,因为它不知道 A 应该是什么(全局定义被本地原型遮蔽,导致它被忽略)。

【讨论】:

  • 不发表评论就否决?这个答案有什么问题?
  • @exilit 你是什么意思?
  • 我的意思是,有人对您的答案投了反对票,但没有发表评论解释他为什么这样做。
  • 不,至少在我看来不是。这就是我投票的原因。
  • @exilit 谢谢!
猜你喜欢
  • 2020-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-10
  • 2018-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多