考虑以下代码片段
constexpr int divide( int x, int y)
{
return (x/y);
}
constexpr int div_by_zero( int x)
{
return (x/0);
}
案例1:
int x = divide(10,0);
这成功编译(使用gcc和clang)但会产生以下运行时错误。
浮点异常(核心转储)
案例2:
int y = div_by_zero(10);
这会产生编译器错误,
(g ++ -std = c ++ 17)除以零不是常量表达式
注意:即使在这种情况下,clang也不会抛出错误
还有编译器警告:
(clang ++ -std = c ++ 17),除以零是未定义的 [-Wdivision被零]
(g ++ -std = c ++ 17),除以零[-Wdiv-by-zero]
对于案例1,为什么即使在编译期间第二个参数的值已知(即零),编译器也不会抱怨?
示例here 分(10,2);生成汇编代码
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], 5
分(10,0);生成汇编代码
mov esi, 0
mov edi, 10
call divide(int, int)
当输入为零时,调用divide()。当非零值是函数的输入时,在编译时评估相同的值。如果编译器能够找到并调用该方法而不是评估,为什么不能抛出错误?
答案 0 :(得分:2)
divide(10, 0)
没有发出警告的事实是实施质量(QoI)问题。你可以用gcc提交一个关于它的bug。但请注意,这种任意常数跟踪并不总是可行的。
但是div_by_zero(10)
没有编译的事实略有不同。来自[dcl.constexpr]/5:
对于既不是默认也不是模板的constexpr函数或constexpr构造函数,如果不存在参数值,则函数或构造函数的调用可以是核心常量表达式的计算子表达式,或者对于构造函数,某些对象的常量初始化程序([basic.start.static]),程序格式错误,无需诊断。
构成核心常量表达的限制之一是它无法调用undefined behavior,其中包括division by zero。
因此,对于divide()
,您可以传递一些参数,使调用成为有效的常量表达式。但对于div_by_zero()
,不存在任何参数,可以使其成为常量表达式 - 这会使程序格式错误。
答案 1 :(得分:-1)
您的函数是constexpr
,这意味着当上下文允许时,它们将被评估为constexpr
(编译时) 。在您的使用中,上下文不是constexpr
,因此将计算推迟到运行时。 https://godbolt.org/g/Jo5GyL
int x = div(10, 0); // x isn't a constexpr var, calculation in runtime
constexpr int y = div(10, 0); // y is constexpr, calculation in compile-time + error
在第二个例子中,除法部分是明确定义的:x / 0
。这就是为什么编译器知道这会失败并报告错误即使在编译时没有调用该函数(再次,var constexpr
中缺少y
)