当计算constexpr时抛出异常会发生什么? [英] What happens when an exception is thrown while computing a constexpr?

查看:112
本文介绍了当计算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 a exception
constexpr int f42 = f(42); //不行,但会发生什么?
}
catch(std :: exception const& ex){
std :: cout< ERROR:<< ex.what()<< \\\
;
}
}

第一次调用 f ()只是显示可以计算 constexpr 。第二次调用 f()不用于初始化 constexpr 并抛出运行时异常。第三次调用 f()用于初始化一个 constexpr ,但这一点不会到达,被抛出。但是,在这种情况下应该发生什么?我希望 catch -clause中的处理程序被执行,但 gcc clang 产生编译时错误。

解决方案

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


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


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


<除非它涉及以下之一作为潜在求值的子表达式,否则条件表达式核心常量表达式,但是[...]


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

    / li>
  • [...]




  • 函数调用替换 constexpr 函数定义如下(§7.1.5/ 5):


    函数调用替换调用 constexpr 函数[...]表示将每个参数隐式转换为相应的参数类型,如同通过copy-initialization,将每个使用的转换表达式替换在 function-body 中的相应参数,并且[...]隐含地将生成的返回表达式或
    braced-init-list 转换为函数的返回类型如果通过复制初始化。


    正如我们上面看到的(§5.19/ 2),未评估的条件操作的子表达式不考虑。 f(42)不是常量表达式,因为在 f 上执行函数调用替换时,在所评估的条件运算的一侧有一个 throw 表达式。另一方面,对于 f(41) throw 结束于 isn' t



    因此程序错误。无论初始化程序是否实际达到,因为程序不应该编译。


    When computing constant expressions to initialize a constexpr it is possible to throw exceptions. For example here is an example where the computation of a constant expression is guarded against overflow:

    #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";
        }
    }
    

    The first call to f() just shows that a constexpr can be computed. The second call to f() isn't used to initialize a constexpr and throws a run-time exception. The third call to f() is used to initialize a constexpr but that point won't ever reached because an exception is thrown. However, what should happen in this case? I would expect the handler in the catch-clause is executed but both gcc and clang produce a compile-time error.

    解决方案

    The initialiser for a constexpr variable must be a constant expression (C++11 §7.1.5/9):

    A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized. If it is initialized by a constructor call, [...]. Otherwise, or if a constexpr specifier is used in a reference declaration, every full-expression that appears in its initializer shall be a constant expression.

    Note the following requirements for a constant expression (§5.19/2):

    A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression, but subexpressions of [...] conditional operations that are not evaluated are not considered

    • [...]

    • an invocation of a constexpr function with arguments that, when substituted by function invocation substitution (7.1.5), do not produce a constant expression;

    • [...]

    • a throw-expression (15.1).

    Function invocation substitution for a constexpr function is defined as follows (§7.1.5/5):

    Function invocation substitution for a call of a constexpr function [...] means implicitly converting each argument to the corresponding parameter type as if by copy-initialization, substituting that converted expression for each use of the corresponding parameter in the function-body, and [...] implicitly converting the resulting returned expression or braced-init-list to the return type of the function as if by copy-initialization. Such substitution does not change the meaning.

    As we saw above (§5.19/2), subexpressions of conditional operations that are not evaluated are not considered. f(42) is not a constant expression because when you perform function invocation substitution on f, it results in an expression with a throw expression on the side of the conditional operation that is evaluated. On the other hand, for f(41), the throw ends up on the side that isn't evaluated.

    So the program is ill-formed. It doesn't matter whether the initialiser is actually reached or not because the program shouldn't compile.

    这篇关于当计算constexpr时抛出异常会发生什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆