在计算constexpr时抛出异常会发生什么?

时间:2014-01-01 15:40:32

标签: c++ exception c++11 language-lawyer constexpr

计算常量表达式以初始化constexpr时,可能会抛出异常。例如,这里是一个示例,其中常量表达式的计算被防止溢出:

#include <iostream>
#include <stdexcept>

constexpr int g(int n, int n0, int n1) {
    return n == 0? n1: g(n - 1, n1, n0 + n1);
}

constexpr int f(int n) {
    return n < 42? g(n, 0, 1): throw std::out_of_range("too big");
}

int main()
{
    try {
        constexpr int f41 = f(41); // OK: constexpr
        int           f43 = f(43); // OK: throws an exception
        constexpr int f42 = f(42); // not OK but what happens?
    }
    catch (std::exception const& ex) {
        std::cout << "ERROR: " << ex.what() << "\n";
    }
}

第一次调用f()只是表明可以计算constexpr。对f()的第二次调用不用于初始化constexpr并抛出运行时异常。对f()的第三次调用用于初始化constexpr,但由于抛出异常,因此无法达到该点。但是,在这种情况下会发生什么?我希望catch - 子句中的处理程序已执行,但gccclang都会产生编译时错误。

1 个答案:

答案 0 :(得分:12)

constexpr变量的初始化必须是常量表达式(C ++11§7.1.5/ 9):

  

对象声明中使用的constexpr说明符将对象声明为const。这样的对象应具有文字类型并应初始化。如果它是由构造函数调用初始化的,[...]。否则,或者如果在引用声明中使用constexpr说明符,则其初始值设定项中出现的每个完整表达式都应为常量表达式。

请注意常量表达式的以下要求(§5.19/ 2):

  

条件表达式是一个核心常量表达式,除非它涉及下列其中一个作为潜在评估的子表达式,但子条件表达式为条件运算不评估不被视为

     
      
  • [...]

  •   
  • 调用带有参数的constexpr函数,当被函数调用替换(7.1.5)替换时,不生成常量表达式;

  •   
  • [...]

  •   
  • throw-expression (15.1)。

  •   

constexpr函数的函数调用替换定义如下(第7.1.5 / 5节):

  

函数调用替换用于调用constexpr函数[...]意味着将每个参数隐式转换为相应的参数类型,就像通过复制初始化一样,替换该转换后的表达式对于 function-body 中每个对应参数的使用,以及[...]将生成的返回表达式或 braced-init-list 隐式转换为返回类型该功能就像通过复制初始化一样。这种替代不会改变其含义。

如上所述(§5.19/ 2),未考虑未评估的条件操作的子表达式。 f(42)不是常量表达式,因为当您在f上执行函数调用替换时,它会导致在计算的条件运算一侧使用throw表达式的表达式。另一方面,对于f(41)throw最终会在评估的一侧。

因此该计划形成不良。是否实际达到初始化并不重要,因为程序不应该编译。