C++ 函数范围中的静态constexpr是MSVC';什么是编译器错误?
我的理解是,函数作用域C++ 函数范围中的静态constexpr是MSVC';什么是编译器错误?,c++,function,scope,static,constexpr,C++,Function,Scope,Static,Constexpr,我的理解是,函数作用域静态constexpr是在编译时计算的。如果是这种情况,MSVC对以下错误有何理由(如有): int main() { int i = 5; switch (i) { // Original question: static constexpr int j = 7; // legal? // My actual use case, which may not be legal. sta
静态constexpr
是在编译时计算的。如果是这种情况,MSVC对以下错误有何理由(如有):
int main()
{
int i = 5;
switch (i)
{
// Original question:
static constexpr int j = 7; // legal?
// My actual use case, which may not be legal.
static constexpr int k[2] = { 7, 4 };
default:
case 0:
break;
}
return 0;
}
testapp.cpp(10):错误C2360:“案例”标签跳过了“j”的初始化
如果这是一个非constexpr,那么是的,这是一个有效的投诉。但是,由于constexpr是在编译时计算的,因此不需要在声明站点执行任何操作
--编辑--
向马丁·邦纳致歉,他的回答被删除了,因为它不适用于我原来的问题
我的实际用例是第二个:带有初始值设定项列表的constexpr数组。从我在引用的标准中看到的情况来看,我的第一个静态constexpr scalar int案例并没有被禁止。然而,我试图做的似乎是不正确的
如果这是真的,那为什么呢?constexpr的全部目的不是在编译时对事物进行求值,因此控件是否真的通过了声明并不重要。这是一个MSVC错误
跳入变量声明+初始值设定项的范围是非法的(例如案例0:int j=5;
),但规则还规定,控件第一次通过其声明时,将初始化静态
。然而,根据§6.4.2/6[stmt.switch],和声明允许存在于switch语句体中
“…声明可以出现在
开关语句。”
因此,static
是本例中一切正常的原因
如果在switch语句的开头有一个非静态声明(没有初始化),那么就完全可以了;在初始化过程中跳过声明会导致编译器出现错误
switch (i)
{
int j;
case 0:
j = 2;
std::cout << 0 << std::endl;
break;
default:
j = 3;
std::cout << j << std::endl;
break;
}
:§6.7/3[标准dcl]:
可以传输到块中,但不能以通过初始化绕过声明的方式传输
我认为这可以更好地解释:
如果控制权转移进入任何自动变量的作用域(例如,通过跳过声明语句),则程序是格式错误的(无法编译),除非所有输入作用域的变量
1) 未使用初始值设定项声明的标量类型
正如@PasserBy在一条评论中暗示的那样,
constexpr
并没有像您希望的那样产生多大影响。在静态constexpr int a=10之后
,读取a
值的代码可以在编译时进行优化,以直接使用10
,编译器将确保初始化器10
是编译时常量,但仍有一个对象在某些情况下可能需要在运行时实际存储,包括初始化。例如,当取其地址时
块作用域static
s的初始化可能提前发生,与文件作用域对象的初始化方式相同,也可能在执行到达声明时发生:
N4140[dcl.stmt]p4:
[…]在允许实现静态初始化的相同条件下,允许实现使用静态或线程存储持续时间对其他块作用域变量执行早期初始化
命名空间范围内具有静态或线程存储持续时间的变量(3.6.2)。否则,在控件第一次通过其声明时初始化该变量;此类变量在初始化完成时被视为已初始化。[……]
现在,@MartinBonner删除的答案引用了[dcl.stmt]p3:
可以传输到块中,但不能以通过初始化绕过声明的方式传输。一种程序,从一个具有自动存储持续时间的变量不在范围内的点跳90到另一个点
除非变量具有标量类型、具有普通默认构造函数和普通析构函数的类类型、其中一种类型的cv限定版本或其中一种类型的数组,否则它在作用域中的点是格式错误的
前面的类型和声明没有初始值设定项(8.5)
第二句话只处理具有自动存储持续时间的对象,但第一句话没有。您正在以一种可能通过初始化绕过声明的方式传输到块中。唯一的问题是,没有必要通过初始化来绕过声明:如果在早期执行初始化,则不会绕过任何初始化
在这种特殊情况下,我希望所有合理的实现都能执行早期初始化,但这不是必需的,因此我认为错误消息是允许的。但我怀疑这不是MSVC的预期行为:
下面是一个您肯定希望出现错误的示例:
int &f() { static int i; return i; }
int main() {
switch (0) {
static int &i = f();
case 0:
return i;
}
}
在这里,跳过变量
i
的初始化,因此,访问未初始化的引用,从而在多个编译器上触发分段错误。然而在本例中,MSVC并没有显示编译错误。在错误无害的情况下发出错误消息是没有意义的,但在错误有害的情况下忽略错误消息。因此,我怀疑您收到的错误消息不是故意的,值得报告为bug。fyi g++5.1.0将其编译为干净的。我要说,编写switch语句的方式非常奇怪。在它之外引入另一个作用域来放置变量<代码>{static constexpr int j=7;swtich(i){…}constexpr意味着它在编译时是已知的,但除此之外,它的语义与任何其他变量(加上const ness)相同@AndyG当然,我可以在开关上方立即声明它,这就是我目前所做的。但是,这是一个保持宣言接近其用途的问题。C++使我们有能力在中间块中声明变量,这正是这个原因。
int &f() { static int i; return i; }
int main() {
switch (0) {
static int &i = f();
case 0:
return i;
}
}