constexpr
说明符是否暗示函数的noexcept
说明符? Answer到the similar question说"是"关于inline
说明符,但Eric Niebler's article让我想知道当前的可能答案。在我看来,答案取决于使用constexpr
函数的上下文:它是常量表达式上下文还是运行时上下文,即在编译时是否已知函数的所有参数。
我希望答案是"是",但simple check表明事实并非如此。
constexpr
bool f(int) noexcept
{
return true;
}
constexpr
bool g(int)
{
return true;
}
static_assert(noexcept(f(1)));
static_assert(noexcept(g(2))); // comment this line to check runtime behaviour
#include <cassert>
#include <cstdlib>
int
main(int argc, char * [])
{
assert(noexcept(f(argc)));
assert(noexcept(g(argc)));
return EXIT_SUCCESS;
}
答案 0 :(得分:18)
不可能不是这种情况,因为并非constexpr函数的每个inovocation都必须能够被评估为核心常量表达式的子表达式。我们只需要一个允许这个的参数值。因此,只要我们有一个不调用该分支的参数值,constexpr函数就可以包含 throw 语句。
这在C ++ 14标准部分7.1.5
草案中有所说明constexpr说明符[dcl.constexpr]告诉我们constexpr函数允许的内容:
constexpr函数的定义应满足以下约束条件:
它不应是虚拟的(10.3);
其返回类型应为文字类型;
其每个参数类型都应为文字类型;
其函数体应为= delete,= default或不包含的复合语句
asm-definition,
goto语句,
试用版,或
非文字类型或静态或线程存储持续时间或其变量的定义 没有进行初始化。
我们可以看到它并不禁止throw
,事实上禁止很少,因为Relaxing constraints on constexpr functions提案已成为C ++ 14的一部分。
下面我们看到规则说如果至少存在一个参数值,则constexpr函数是格式良好的,这样它就可以被评估为核心常量表达式的子表达式:
对于非模板,非默认的constexpr函数或非模板,非默认的,非继承的 constexpr构造函数,如果不存在参数值,则调用函数或构造函数 可以是核心常数表达式的评估子表达式(5.19),该程序是不正确的;没有 需要诊断。
在本段下面我们有以下示例,其中显示了此案例的完美示例:
constexpr int f(bool b)
{ return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required
所以我们期望以下示例的输出:
#include <iostream>
constexpr int f(bool b) { return b ? throw 0 : 0; }
int main() {
std::cout << noexcept( f(1) ) << "\n"
<< noexcept( f(0) ) << "\n" ;
}
是( see it live with gcc ):
0
1
Visual Studio via webcompiler也会产生相同的结果。正如hvd所说,clang有错误报告noexcept should check whether the expression is a constant expression记录的错误。
缺陷报告1129
Defect report 1129: Default nothrow for constexpr functions问同样的问题:
不允许constexpr函数通过异常返回。这应该被识别,并且声明constexpr而没有显式异常规范的函数应该被视为声明为noexcept(true)而不是通常的noexcept(false)。对于声明constexpr而没有显式异常规范的函数模板,当且仅当在给定实例化上遵守constexpr关键字时,才应将其视为noexcept(true)。
并且回复是:
前提不正确:只有在需要常量表达式的上下文中调用constexpr函数时才禁止异常。用作普通函数,它可以抛出。
并修改了5.3.7 [expr.unary.noexcept]第3段子弹1(另外注意到重点):
对函数,成员函数,函数指针或成员函数指针的潜在求值call80,它没有非抛出异常规范(15.4 [except.spec]),除非调用是常量表达式(5.20 [expr.const]),
答案 1 :(得分:5)
noexcept
constexpr
的{p> It is said:
如果表达式包含对任何类型的没有非抛出异常规范的函数的调用,则结果为false,除非它是常量表达式。
此外,大约constexpr
,it is true:
noexcept运算符总是为常量表达式返回true
在任何情况下,似乎并不意味着noexcept
说明符会强制包含表达式的noexcept
说明符,因为有人在评论中显示了反例并且您也进行了验证。
无论如何,从文档中,有关constexpr
和constexpr int f(int i) {
return i == 0 ? i : f(i - 1);
}
int main() {
noexcept(f(512));
return noexcept(f(0)) == noexcept(f(0));
}
之间关系的以下有趣说明:
因为noexcept运算符总是为常量表达式返回true,所以它可用于检查constexpr函数的特定调用是否采用常量表达式分支
编辑:GCC示例
感谢@hvd对GCC有关我最后一句话的有趣评论/例子。
0
上面的代码返回noexcept(f(512))
,并警告语句1
无效
如果声明该声明无效,则返回值将更改为case object
。
编辑:clang已知错误
再次感谢@hvd也为this链接,这是关于 clang 中有关问题中提到的代码的一个众所周知的错误。
引用错误报告:
C ++的书,[expr.unary.noexcept] p3:
“如果在潜在评估的上下文中,表达式将包含对函数,成员函数,函数指针或不具有非投掷的成员函数指针的潜在评估调用,则noexcept运算符的结果为false异常规范(15.4),除非调用是常量表达式(5.19)“。
我们没有实施最后一句话。
答案 2 :(得分:2)
不,一般情况下不会。
可以在非constepr上下文中调用constexpr函数,在该上下文中允许它抛出异常(当然,除非您手动将其指定为noexcept(true)
)。
但是,作为常量表达式的一部分(例如在您的示例中),它的行为应该像指定为noexcept(true)
一样(当然,如果对表达式的求值会导致抛出异常)由于程序尚未运行,因此无法调用std::terminate
,而是导致编译时错误)。
正如我所料,您的示例不会触发MSVC和g ++的静态断言。我不确定,这是否是铿锵声中的错误,或者我对标准中的某些内容有所了解。
答案 3 :(得分:2)
您可以在constexpr函数中抛出异常。它被设计为这样,以便实现者可以向编译器指示错误。请考虑您具有以下功能:
constexpr int fast_sqrt(int x) {
return (x < 0) ? throw invalid_domain() : fast_sqrt_impl(x);
}
在这种情况下,如果x为负数,我们需要立即停止编译并通过编译器错误向用户指出问题。这遵循了编译器错误优于运行时错误(快速失败)的想法。
C ++标准在(5.20)中说明了这一点:
条件表达式e是核心常量表达式,除非e的评估遵循规则 抽象机器(1.9),将评估以下表达式之一:
- 一个throw-expression(5.17)