c++ 为什么我们不能在Switch内部初始化一个“constexpr变量”(在非最后一个SWITCH CASE下面)?

nhhxz33t  于 2022-12-24  发布在  其他
关注(0)|答案(2)|浏览(281)

正如我们所知,我们不能初始化switch中任何一个case中的变量,除非它是相应switch的最后一个case,因为变量的初始化确实需要在运行时执行定义(因为初始化器的值必须在该点确定),
但是
我们还知道,在编译过程中,constexpr变量将被初始化,或者在代码中被替换为它的值。
因此,我尝试了以下代码,即初始化case 2中的constexpr变量Z(这不是switch的最后一个case),但我得到一个错误,说明如下:
交叉“constexpr const int z”的初始化24|
常数表达式整数z{ 4 };//非法:如果存在后续案例,则不允许初始化
有人能解释一下这个错误背后的原因吗?
提前感谢您!

#include <iostream>

int main()
{
switch (1)
{
    int y; 
    
    case 1:
        y = 4; 
        break;

    case 2:
        constexpr int z{ 4 }; // ERROR
        break;

    case 3:
        y=5;
        break;
}
return 0;
}
plupiseo

plupiseo1#

根据switch statement的c++参考
因为不允许控制转移进入变量的作用域,所以如果在语句内部遇到声明语句,则必须在其自己的复合语句中确定其作用域。
这意味着你实际上可以初始化一个局部变量,不管它是否在作用域内。例如,对你的代码做一个小的修改就可以编译:

#include <iostream>

int main()
{
switch (1)
{
    int y; 
    
    case 1:
        y = 4; 
        break;

    case 2:
        {
            constexpr int z{ 4 }; // No more ERROR
            break;
        }
    case 3:
        y=5;
        break;
}
return 0;
}

检查它live on Coliru
switch语句跳转到一个匹配的case,非常像goto statement,然后,像goto语句一样,如果跳转到给定的行,你"跳过"变量的声明/初始化,会发生什么,这是不清楚的。
请看下面的例子:

int main()
{
    goto jump;
    int i = 1;
    
jump:

}

这是不能编译的,因为在执行跳转时,编译器不知道如何处理变量i。注意,如果没有跳转,就可以在jump标签后使用变量i。但是如果只是"跳过"初始化,代码就会出现格式错误。switch语句本质上是一个goto,因此它有同样的限制。你声明的变量是constexpr的事实并不改变这个限制。

42fyovps

42fyovps2#

具有自动存储持续时间的变量的初始化发生在控制转移到它上面时,而不管它是否是constexpr变量。
考虑以下代码(如果允许在switch的主体中初始化constexpr变量):

void f(const int*);
switch (cond) {
case 1:
    constexpr int z{ 4 };
    f(&z);
    break;
case 2:
    f(&z);
    break;
}

即使z是一个constexpr变量,编译器仍然会把它放在堆栈上,把它的地址传递给f,并且在运行时它在堆栈上的值会被赋值为4。但是2的情况怎么样呢?编译器在进入开关时会在堆栈上分配z。但是逻辑上初始化器只有在它进入第一种情况时才“运行”。
你不能在编译时“初始化”一个constexpr变量,因为它将被放置在堆栈中。是否将它完全删除并替换它的所有出现是一个优化(如果它的地址被获取,这并不总是可能的)。

相关问题