使用 g++ 编译器,这两个都可以编译:
constexpr double d1 = 2.0 / 1.0;
const double d2 = 2.0 / 0.0;
但事实并非如此:
constexpr double d2 = 2.0 / 0.0;
编译时错误是:
error: ‘(2.0e+0 / 0.0)’ is not a constant expression
请注意,,确认这是不允许的。
我不明白为什么不允许这样做。请记住,是constexpr
,编译器在编译时清楚地知道在 内constexpr
是否允许除以零。那么,在允许的情况下……为什么在 内不允许呢constexpr
?
编辑:有人认为这个问题类似于。我在这里的问题旨在更加集中(为什么前两个表达式似乎被允许,但第三个表达式却不允许)?另一个问题的答案似乎并没有阐明这一点:一些答案似乎暗示所有三个表达式都是 UB(因此是不允许的?我不认为我相信这一点;但无论如何,constexpr 似乎有一些特定的东西会产生影响,因此这个问题的重点是 constexpr)。
这个问题也类似于其接受的答案(本质上“它是 UB 所以任何事情都可以”)根本没有帮助。
24
最佳答案
1
ISO C++ 标准明确规定,对于整数和浮点类型来说,除以零都有未定义的行为。
参见。
如果根据核心语言规范,表达式的求值具有未定义的行为,则其求值涉及该求值的表达式将被指定为不是(核心)常量表达式。
因此,在编译时任何除以零都是不可能的。
尽管按照 C++ 标准,除以零是未定义的,但浮点类型的实现通常遵循 IEEE 754,该标准定义了除以零的结果。这当然没问题。如果程序根据 C++ 标准具有未定义的行为,那么编译器可以自由地为其赋予任何语义。特别是它可以承诺遵守更严格的规则,例如另一个规范。
然而,在常量表达式中,语言被有意限制为标准明确定义的内容。编译器无法constexpr
像在运行时那样扩展行为。如果constexpr
变量的初始化不是常量表达式,则程序格式不正确。
虽然标准从未要求编译器编译失败。如果程序格式不正确,那么唯一的要求是编译器应发出一个诊断。这也可能只是一个警告,然后编译器可以简单地在编译时执行除法,就像在运行时一样。
在某些情况下,程序的语义不仅受格式正确性的影响,还受表达式是否为常量表达式的影响。在这种情况下,编译器需要为程序提供正确的语义。在这种情况下,警告和不同的行为是不可能的。
2
-
1它不是常量表达式这一事实可以被格式良好的 SFINAE 程序利用。符合规范的编译器可以允许它,否则会发出警告(GCC 会针对某些形式上缩小但显然安全的转换执行此操作),但这种不一致可能会令人恐惧。
– -
“那当然没问题。” 🙂 这实际上是一个关键点,而且根本没有被普遍理解。常见的(错误)理解似乎是,这实际上并不好——也就是说,UB 意味着符合规范的编译器无法保证更严格的规范。因此,许多关于 UB 的对话过早地以“这是 UB,所以什么都可以”结束,这非常无益 感谢您更细致入微的解释;接受。
–
|
–
–
C
文档。我猜这是 C++ 和 C 分歧的地方。–
std::numeric_limits::is_iec559
为真… 我不确定这里的规则是什么,但规则是,你的编译器正确地拒绝了 constexpr 中的除以零。–
–
|