【问题标题】:Runtime errors with constexprconstexpr 的运行时错误
【发布时间】:2018-02-05 13:38:25
【问题描述】:

考虑下面的代码 sn-ps

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),除以零是未定义的 [-W除零]

(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()。当非零值是函数的输入时,在编译时也会评估相同的值。如果编译器能够找出并调用方法而不是评估,为什么不能抛出错误?

【问题讨论】:

    标签: c++ c++11 c++14 constexpr c++17


    【解决方案1】:

    divide(10, 0) 没有发出警告的事实是实施质量 (QoI) 问题。你可以向 gcc 提交一个关于它的错误。但请注意,这种任意的常量跟踪并不总是可行的。

    div_by_zero(10) 无法编译的事实略有不同。来自[dcl.constexpr]/5

    对于既不是默认值也不是模板的 constexpr 函数或 constexpr 构造函数,如果不存在参数值,则函数或构造函数的调用可以是核心常量表达式的求值子表达式,或者,对于构造函数,某些对象([basic.start.static])的常量初始化程序,程序格式错误,不需要诊断。

    构成核心常量表达式的一个限制是它不能调用undefined behavior,其中包括division by zero

    所以对于divide(),您可以传递一些参数,使调用成为有效的常量表达式。但是对于div_by_zero(),不存在您可以提供的 any 参数以使其成为常量表达式 - 这会使程序格式错误。

    【讨论】:

      【解决方案2】:

      您的函数是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 y 中缺少 constexpr)。

      【讨论】:

        猜你喜欢
        • 2013-12-26
        • 1970-01-01
        • 1970-01-01
        • 2019-12-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多