如何使 consteval 函数失败?

问题描述

我有以下功能

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(使用宏它会不那么可怕),但我更喜欢在函数中工作的解决方案。这种情况有没有办法触发编译失败?

解决方法

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

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

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,TSize> &arr) {
  for (size_t i = 0; i < TSize; ++i) {
    if (arr[i] == someEnum) {
      return i;
    }
  }

  trigger_consteval_failure("failed to find someEnum");
}

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

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

,

您可以简单地省略 return

template <size_t TSize>
consteval size_t indexOf(SomeEnum 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,TSize> &arr) {
  for (size_t i = 0; i < TSize; ++i) {
    if (arr[i] == someEnum) {
      return i;
    }
  }
  throw 42; // or more meaningful exception
}

Demo

,

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

但是,如果你不能这样做,那么就返回 TSize;它应该像结束迭代器一样。