constexpr是否暗示noexcept?

时间:2016-01-01 11:39:18

标签: c++ inline c++14 constexpr noexcept

constexpr说明符是否暗示函数的noexcept说明符? Answerthe 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;
}

4 个答案:

答案 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,除非它是常量表达式。

此外,大约constexprit is true

  

noexcept运算符总是为常量表达式返回true

在任何情况下,似乎并不意味着noexcept说明符会强制包含表达式的noexcept说明符,因为有人在评论中显示了反例并且您也进行了验证。

无论如何,从文档中,有关constexprconstexpr 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)