【问题标题】:Why is variable declared in an if-statement still in scope in else block? [duplicate]为什么在 if 语句中声明的变量仍在 else 块的范围内? [复制]
【发布时间】:2021-04-12 16:46:37
【问题描述】:

考虑这段代码:

if (int* p = nullptr) {
}
else {
    std::cout << *p << std::endl;
}

这与带有 -std=c++17-Wall-Wextra-pedantic-errors 的 clang 7.0.1 一起编译,不会生成任何警告。

我有两个问题:

  1. 这怎么可能?我一直认为这些变量的范围以 if 块结束。如果可能的话,我将不胜感激引用标准。

  2. 如果它是合法的,它至少不应该产生一个警告吗?这种特性有没有合理的应用?

【问题讨论】:

  • "我一直认为此类变量的作用域以 if 块结尾。" else is 部分if 块的。
  • 我认为这与初始化后出现左括号有关
  • cppreference's description of if 提到“条件引入的名称范围,如果是声明,就是两个语句体的组合范围”
  • 这对我来说似乎完全合理。在您的示例中,如果条件是某个复杂表达式的真实性,这为您提供了一种方法来声明一个变量来保存该表达式的值,并且您可能想在两个子句中的任何一个中知道该值是什么。
  • @NateEldredge • 你可以if (int c = getchar(); c == EOF) ...

标签: c++ if-statement language-lawyer variable-declaration


【解决方案1】:

为什么在 if 语句中声明的变量仍然在 else 块的范围内?

因为标准说它是。之所以这样设计,大概是因为它有用。

  1. 这怎么可能?

目前还不清楚为什么它不可能。

我一直认为此类变量的作用域以 if-block 结尾。

你猜错了。

如果可能的话,我会很感激引用标准。 [语言律师]

最新草稿说:

[stmt.select.general]

选择语句选择多个控制流之一。 选择声明:

  • ...
  • if constexpropt (init-statementopt 条件) 语句 else 语句
  • ...

请注意,整个if (condition) statement else statement 是一个选择语句。

[basic.scope.block]

每个

  • 选择或迭代语句([stmt.select]、[stmt.iter]),
  • ...

引入包含该语句或处理程序的块作用域。

请注意,条件直接在 if 语句中,而不是在子语句中,因此其中的声明一直延伸到整个块范围的末尾,其中包含两个 if -sub-statement 和 else-sub-statement(不是这些子语句的标准名称)。

还有一个非常清楚的例子展示了格式错误的重新声明,并顺便显示了这种声明的范围:

if (int x = f()) {
  int x;            // error: redeclaration of x
}
else {
  int x;            // error: redeclaration of x
}

  1. 如果它是合法的,它至少不应该产生一个警告吗?

是的,如果编译器能够在编译时检测到所有可证明的空指针间接寻址,那就太好了。可能值得提交有关此极端案例的功能请求。

这种特性有什么合理的应用吗?

当然。以下捏造的例子对我来说似乎是合理的:

if (Response r = do_request()) {
    log << "great success. content: " << r.content;
} else {
    log << "someone broke it :( error: " << r.error_code;
}

或者如果你不喜欢隐式转换:

if (Response r = do_request(); r.is_success()) {

【讨论】:

  • 我见过的最好的解释是if (x) {y();} else {z();}实际上 {bool x_cond = x; if (x_cond) {y();} else {z();}},它解释了范围。
  • @MooingDuck 有一个场景打破了这个类比:if (int x = 1) {int x = 2;}。这给了我error: redefinition of 'x'
  • 我接受了这个答案,因为它涵盖了我提到的所有要点,不过有一个挑剔:“是的,如果编译器能够在编译时检测到所有可证明的空指针间接,那就太好了。”当然那会很好,但这不是我的意思——我只是认为(并且仍然认为,尽管提供了示例)在进一步的 else 块中引用变量会妨碍代码的清晰度(即很少需要)并且 that fact 应该用警告标记。不过这只是我的看法。
  • @mdx 在声明的范围内标记变量的使用会适得其反,因为这通常是唯一可以引用变量的地方。
  • @eerorika 我明白你的意思,但我不会这么说。我发现这个功能比生产更危险。一个不受欢迎的例子是复制一个较长的 if-ladder 的 else-if 分支并忘记更改其中的变量名。以相当低的价格 IMO 将此类情况标记为警告将是一件有用的事情。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-04-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-15
  • 2021-04-26
相关资源
最近更新 更多