Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/cmake/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 潜在常数表达式求值期间的未定义行为_C++_C++11_Language Lawyer - Fatal编程技术网

C++ 潜在常数表达式求值期间的未定义行为

C++ 潜在常数表达式求值期间的未定义行为,c++,c++11,language-lawyer,C++,C++11,Language Lawyer,考虑一下这个计划: #include <stdio.h> #include <stdlib.h> #include <limits.h> extern int i; struct S { S() { if (i == 0) { puts("Hello, world!"); exit(0); } } }; S s; int i = 1 + 2 * INT_MI

考虑一下这个计划:

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

extern int i;

struct S {
    S() {
        if (i == 0) {
            puts("Hello, world!");
            exit(0);
        }
    }
};

S s;

int i = 1 + 2 * INT_MIN;

int main() { }
#包括
#包括
#包括
外部国际一级;
结构{
S(){
如果(i==0){
放置(“你好,世界!”);
出口(0);
}
}
};
S S;
int i=1+2*int_MIN;
int main(){}
正如我对表达式求值的理解,这是一个严格一致的程序,它打印“Hello,world!”,然后退出,并且从不实际求值
I

3.6.2非局部变量的初始化[basic.start.init]

[……]

具有静态存储持续时间(3.7.1)或线程存储持续时间(3.7.2)的变量应在进行任何其他初始化之前进行零初始化(8.5)

执行常量初始化:

--[……]

--如果具有静态或线程存储持续时间的对象未通过构造函数调用初始化,并且其初始值设定项中出现的每个完整表达式都是常量表达式

零初始化和常量初始化统称为静态初始化;所有其他初始化都是动态初始化。静态初始化应在任何动态初始化发生之前进行。具有静态存储持续时间的非局部变量的动态初始化可以是有序的,也可以是无序的。[…]在单个翻译单元中定义了有序初始化的变量应按照其在翻译单元中定义的顺序进行初始化。[……]

5.19常量表达式[expr.const]

条件表达式是核心常量表达式,除非它涉及 以下为可能计算的子表达式(3.2)[……]:

--[……]

--未在数学上定义或不在其类型的可表示值范围内的结果

[……]

文字常量表达式是文字类型的prvalue核心常量表达式,但不是指针类型。[…]字面常量表达式、引用常量表达式和地址常量表达式统称为常量表达式

因为表达式
1+2*INT_MIN
有符号整数溢出,所以它不是核心常量表达式,因此不是文字常量表达式,因此不是常量表达式。由于
i
的初始化器不是常量表达式,因此会执行动态初始化。
s
的初始化也是动态的,因为它的定义先于
i
,所以它的构造函数首先运行。此时,只执行了零初始化,因此检查
i==0
应评估为true


但是,GCC和clang同意可以将
i
静态初始化为
1
。我的经验是,当这两个人达成一致时,他们是正确的,所以我想知道。。。我的分析有任何部分不正确吗?

我认为这里发生的是编译时计算
1+2*INT\u MIN
,而
I
在静态初始化期间初始化。这在[basic.start.init]/3中是允许的

允许实现使用静态存储执行非局部变量的初始化 作为静态初始化的持续时间,即使不需要静态完成此类初始化,但前提是 那

  • 初始化的动态版本不会更改命名空间的任何其他对象的值 初始化之前的作用域,以及

  • 初始化的静态版本在初始化变量中生成的值与 如果所有不需要静态初始化的变量 动态初始化

因此,即使在常量初始化过程中不需要初始化
i
,它也可以在静态初始化过程中初始化,而静态初始化仍然在动态初始化之前。对于
s
,情况可能也是如此,但我认为编译器不可能做到这一点,因为它有副作用


如前所述,不调用UB,您可以使初始化表达式任意复杂,因此
i
实际上只在动态初始化期间初始化。例如:

int foobar()
{
    return 42;
}

int i = foobar();
打印
你好,世界



附带说明:为了查看
1+2*INT_MIN
由于有符号整数溢出而调用UB,可能需要对表达式求值。这可能会在初始化过程中导致UB。

啊,这很奇怪,因为编译器可以在编译时执行算术。。?奇怪的问题,+1。难道你不需要计算表达式
1+2*INT\u MIN
才能发现它调用了有符号整数溢出吗?(因此已经有了UB?)我不认为是不断的表达(或缺乏表达)导致了你的问题。它似乎将其视为未初始化的变量,这似乎是有意义的,因为
extern
只是告诉编译器变量已在某个地方声明,而您的初始化尚未发生。这意味着
i
是内存中的一些随机位,因此您中彩票的几率比0高,从而按预期退出程序。这与[basic.start.init]/3中的示例不相似吗?也就是说,
I
即使不是必需的,也不能静态初始化吗?非常奇怪。如果我以另一种方式使其不是核心常量表达式(例如,通过调用非
constexpr
函数),那么它将打印“Hello,world!”。这两个编译器似乎都有缺陷。此外,IIRC UB可以追溯,这意味着在包含UB的程序执行中(以及
1+2*INT\u MIN的求值