如何在 constexpr 函数中执行运行时断言?

How can I do a runtime assert in a constexpr function?

据我了解,constexpr 函数可以在编译时和运行时执行,具体取决于整个计算是否可以在编译时完成。

但是,您不能重载此函数以使其具有运行时和编译时对应项。

所以我的问题是,如何放入运行时断言以确保运行时函数的执行与我的 static_assert 一起传递有效参数?

你可以抛出异常。如果在编译时从 constexpr 函数抛出异常,它基本上算作静态断言失败。如果它发生在运行时,它会像往常一样只是一个异常。

这个问题展示了发生这种情况的代码示例:Passing constexpr objects around

还相关:What happens when an exception is thrown while computing a constexpr?

Eric Niebler 在 Assert and Constexpr in C++11, he points out that using assert in a constexpr function is not allowed in C++11 but it is allowed in C++14(As part of the Relaxing constraints on constexpr functions proposal) 中很好地讨论了这个问题并提供了以下片段:

constexpr bool in_range(int val, int min, int max)
{
    assert(min <= max); // OOPS, not constexpr
    return min <= val && val <= max;
}

如果我们必须支持 C++11,那么有一些替代方案。最明显的是 using throw 但正如他所指出的那样,这会将不可恢复的错误变成可恢复的错误,因为您可以捕获异常。

他提出了一些替代方案:

  1. 使用 throw 和 noexcept specifier:

    constexpr bool in_range(int val, int min, int max) noexcept 
    {
      return (min <= max)
        ? min <= val && val <= max
        : throw std::logic_error("Assertion failed!");
    }
    

    如果异常离开函数 std::terminate 将被调用。

  2. 从异常类型的构造函数中调用std::quick_exit:

    struct assert_failure
    {
        explicit assert_failure(const char *sz)
        {
            std::fprintf(stderr, "Assertion failure: %s\n", sz);
            std::quick_exit(EXIT_FAILURE);
        }
    };
    
    constexpr bool in_range(int val, int min, int max)
    {
        return (min <= max)
          ? min <= val && val <= max
          : throw assert_failure("min > max!");
    }
    
  3. 将断言的 lambda 表达式传递给异常类型的构造函数:

    constexpr bool in_range(int val, int min, int max)
    {
        return (min <= max)
          ? min <= val && val <= max
          : throw assert_failure(
              []{assert(!"input not in range");}
            );
    }