当计算constexpr时抛出异常会发生什么? [英] What happens when an exception is thrown while computing a 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):
<除非它涉及以下之一作为潜在求值的子表达式,否则条件表达式是核心常量表达式,但是[...]
调用具有参数的
/ li>constexpr
函数,当用函数调用替换(7.1.5)替换时,不要生成常量表达式;
[...]
函数调用替换 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 asconst
. Such an object shall have literal type and shall be initialized. If it is initialized by a constructor call, [...]. Otherwise, or if aconstexpr
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屋!