【问题标题】:How to fail a consteval function?如何使 consteval 函数失败?
【发布时间】:2021-07-23 00:04:02
【问题描述】:

我有以下功能:

template <size_t TSize>
consteval size_t indexOf(SomeEnum someEnum,
                         const std::array<SomeEnum, TSize> &arr) {
  for (size_t i = 0; i < TSize; ++i) {
    if (arr[i] == someEnum) {
      return i;
    }
  }
  // How to fail here?
  return SOME_DEFAULT_WRONG_VALUE;
}

函数应该失败而不是返回默认值,但我不能抛出异常或调用assert。我可以在每次调用函数时添加static_assert(使用宏会不那么可怕),但我更喜欢在函数中工作的解决方案。有没有办法在这种情况下触发编译失败?

【问题讨论】:

  • 一般这类函数如果找不到数据不会失败,它们会返回一个越界标记(通常是-1)。搜索您的收藏中不存在的东西是完全有效的。
  • 为什么不能抛出异常或者使用assert
  • 您仍然可以通过constexpr 发送throw。我对consteval 不够熟悉,无法确定,但我相信您也应该能够从他们那里获得throw
  • 您能否澄清一下您是否认为consteval 中不允许投掷,或者您是否有不使用throw 的外部要求?
  • @chris:可以。其中一个答案提到了这一点。

标签: c++ c++20 consteval


【解决方案1】:

有没有办法在这种情况下触发编译失败?

如果目标是触发编译失败,那么最简单的做法就是抛出异常。不管异常是什么,因为它实际上不会被抛出,所以抛出异常的行为会触发编译错误,因为在常量评估时间不允许抛出:

template <size_t TSize>
consteval size_t indexOf(SomeEnum someEnum,
                         const std::array<SomeEnum, TSize> &arr) {
  for (size_t i = 0; i < TSize; ++i) {
    if (arr[i] == someEnum) {
      return i;
    }
  }

  throw "failed to find someEnum";
}

如果你想更明确,你可以只拥有一个没有定义的非constexpr 函数:

void trigger_consteval_failure(char const*);

template <size_t TSize>
consteval size_t indexOf(SomeEnum someEnum,
                         const std::array<SomeEnum, TSize> &arr) {
  for (size_t i = 0; i < TSize; ++i) {
    if (arr[i] == someEnum) {
      return i;
    }
  }

  trigger_consteval_failure("failed to find someEnum");
}

在这两种情况下,如果您要在数组中查找 的值,则调用此函数是一个有效的常量表达式。但是,如果 not 找到索引,那么我们最终会做一些现在在常量表达式中允许的事情,并且无论如何这都是一个硬编译错误。

如果我们能在这种情况下产生更好的堆栈跟踪,那就太好了,但我认为实际上没有办法做到这一点。

【讨论】:

  • gcc 拒绝了 throw :( Demo
  • @Jarod42 这是他们在10.3中修复的错误
【解决方案2】:

您可以简单地省略return

template <size_t TSize>
consteval size_t indexOf(SomeEnum someEnum,
                         const std::array<SomeEnum, TSize> &arr) {
  for (size_t i = 0; i < TSize; ++i) {
    if (arr[i] == someEnum) {
      return i;
    }
  }
}

Demo(clang 会警告该方法)。

编译器会拒绝到达该路径的代码。

Demo

throw 异常看起来更干净:

template <size_t TSize>
consteval size_t indexOf(SomeEnum someEnum,
                         const std::array<SomeEnum, TSize> &arr) {
  for (size_t i = 0; i < TSize; ++i) {
    if (arr[i] == someEnum) {
      return i;
    }
  }
  throw 42; // or more meaningful exception
}

Demo

【讨论】:

  • 我会在那里发表评论,让人们知道它是内涵的,或者只是 throw 一个值。
  • @NicolBolas:我会使用 throw,直到我看到 gcc 拒绝它 Demo
  • 那么 GCC 没有正确遵循 C++20。
【解决方案3】:

您应该避免使用索引,而是使用std::find,现在是constexpr。如果你想要一个索引,你可以使用数组的begining 中的指针算术减法来计算索引。

但是,如果您不能这样做,则只需返回TSize;它应该像结束迭代器一样。

【讨论】:

  • 它(几乎)总是最好回答这个问题,因为问题中附加的代码通常只是问题的一个例子,也是传达解决方案的一种方式。
猜你喜欢
  • 2020-12-03
  • 2021-10-26
  • 2023-03-14
  • 2021-12-10
  • 1970-01-01
  • 2019-12-05
  • 2021-02-20
  • 2020-09-25
  • 1970-01-01
相关资源
最近更新 更多