首页 > 解决方案 > 为什么 switch 构造中的声明是合法的?

问题描述

我发现了多个关于在switch构造中定义变量的问题,但我还没有找到这个问题的明确答案。

C++ Primer 一书的第 5.3.2 章说如下:

正如我们所见,switch 中的执行可以跨越 case 标签。当执行跳转到特定情况时,在该标签之前发生在开关内的任何代码都将被忽略。

考虑到这些信息,我不明白为什么下面的示例是合法的。如果控制跳转到false案例,它应该忽略true案例。这意味着,分配给i应该是非法的,因为它从未被声明过。为什么这个构造是合法的?

case true:
    int i;
    break;
case false:
    i = 42;
    break;

标签: c++

解决方案


声明是编译时的事情,运行时发生的事情与该事实无关。 在其声明后的同一或子范围内的i任何点都可见。

没有什么会导致两个案例之间的范围更改,因此无论案例是否执行i,案例中都保持可见。falsetrue

这就是为什么您可能会看到匿名块 ( { }) 用于在 switch 案例中人为地限制范围。这是为了防止这个潜在的问题(尽管在这种情况下它不是问题)。

case true: {
    int i;
    break;
} // Closing this block causes the end of i's lifetime.

case false: {
    i = 42; // Compile-time error; i is no longer in scope.
    break;
}

请注意,您的代码仅通过初始化i. 跳转不能在任一方向跨越初始化。

case true:
    int i = 0;
    break;
case false: // error: jump to case label crosses initialization
    i = 42;
    break;

此外,任何非 平凡类型的变量都不能具有跨越生命周期的情况,即使它没有显式初始化。

case true:
    std::string i;
    break;
case false: // error: jump to case label crosses initialization
    i = "42";
    break;

在这种情况下,解决方法是使用匿名块来限制声明的范围i不跨越多个案例。


相关标准:

可以转移到一个块中,但不能通过初始化绕过声明的方式。除非变量具有标量类型、具有普通默认构造函数和普通析构函数的类类型,否则从具有自动存储持续时间的变量不在范围内的点跳转到它在范围内的点的程序*,这些类型之一的 cv 限定版本,或上述类型之一的数组,并且在没有初始化程序的情况下声明。

-- C++14 (N4296) [stmt.dcl.3]

关于跳跃的脚注(*):

switch语句的条件到case标签的转移在这方面被认为是一个跳跃。


推荐阅读