【问题标题】:throw in constexpr function: do we need wrapping condition?抛出 constexpr 函数:我们需要包装条件吗?
【发布时间】:2020-04-25 01:56:09
【问题描述】:

基本思路是这样的:我有一些constexpr函数,我想用throw来表示错误和惰性编译以避免在正常流程中出现这个错误:

template <size_t N>
auto constexpr find_elt(const std::array<int, N>& a, int k) {
  for (size_t i = 0; i < N; ++i)
    if (k == a[i])
      return i;
  throw "not found";
}

然后:

constexpr int result = find_elt(arr, 4);

通常,如果数组中存在 4,我会在编译时取回它的索引。

如果不是,我将通过throw 指示在编译时查找错误,编译器会产生一个漂亮的错误。

但我注意到奇怪的行为:

Under the latest clang, everything works

Under the latest gcc, everything fails

这个想法合法吗?此代码是否适合我想要实现的目标?哪个编译器在这里告诉我真相?

如果不是,正确的做法是什么?

感谢任何指向 C++ 标准的链接。我通读了与 constexpr 相关的章节,但我有疑问。

【问题讨论】:

  • @cigien question by your link 是关于非常简单的案例,4 年前已在 gcc 6 中修复
  • 我猜这里的想法是一样的,这是一个 gcc 错误。
  • 不清楚。在引用的情况下,抛出是在明确的条件下,一切都是显而易见的。在这种情况下,gcc 可能是正确的,我将重写代码以将 throw 置于某些明确的条件下。
  • 我不确定,但我认为这是一回事。如果你传递一个导致函数抛出的参数,clang 也会失败。

标签: c++ c++17 constexpr


【解决方案1】:

所以:

  1. 根据 constexpr 函数的编译是一个“惰性”过程这一事实,因此仅在表达式替换期间编译器仍进入的范围内执行对 constexpr 函数要求的符合性检查。
  2. 函数分别是一个作用域 - constexpr 的所有规则都必须在其第一级作用域的整个函数体中得到遵守。
  3. 并且由于表达式“throw”不是常量表达式(正如 gcc-10 编译器已经告诉我们的那样),因此没有观察到正确性。

clang 编译器在这个意义上不像 gcc 那样严格。因此,在这场战斗中,我认为 gcc 获胜。他更致力于标准。 另一方面,如果这是一个“懒惰”的过程,那么为什么不应该懒惰到最后呢。好吧,你找到了最终的回报——那为什么还要进一步检查正确性呢? 从这个意义上说,clang 得到了一点。

最后 - C++17 Standard 说了什么?

10.1.5 constexpr 说明符 [dcl.constexpr]

  1. "... 如果不存在任何参数值,使得函数或构造函数的调用可以是核心常量表达式 (8.20) 的评估子表达式,...,则程序格式错误,无诊断必填。

接下来,我们来看看“核心常量表达式”是什么:

8.20 常量表达式 [expr.const]

  1. 表达式是对的求值,遵循抽象机器 (4.6) 的规则,将求值以下表达式之一:

2.22 - 抛出表达式 (8.17)

并注意“不需要诊断”并且编译器不需要提供失败原因的详细解释。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-01-02
    • 2020-06-09
    • 2019-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多