c++ - 抛出 constexpr 函数:我们需要包装条件吗?
问题描述
基本思想是这样的:我有一些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
指出在编译时查找是错误的,并且编译器会产生一个漂亮的错误。
但我注意到奇怪的行为:
这个想法合法吗?此代码是否适合我想要实现的目标?哪个编译器在这里告诉我真相?
如果不是,那么正确的方法是什么?
感谢任何指向 C++ 标准的链接。我通读了与 constexpr 相关的章节,但我对此表示怀疑。
解决方案
所以:
- 根据 constexpr 函数的编译是一个“惰性”过程的事实,那么仅在表达式替换期间编译器仍然进入的范围内执行对 constexpr 函数要求的符合性检查。
- 一个函数分别是一个范围 - constexpr 的所有规则都必须在其第一级范围内的整个函数体中得到遵守。
- 并且由于表达式“throw”不是常量表达式(正如 gcc-10 编译器已经告诉我们的那样),因此没有观察到正确性。
从这个意义上说,clang 编译器不像 gcc 那样严格。因此,在这场战斗中,我认为 gcc 获胜。他更致力于标准。另一方面,如果这是一个“懒惰”的过程,那么为什么不应该懒惰到最后呢。好吧,你找到了最终的回报——那为什么还要进一步检查正确性呢?从这个意义上说,clang 得到了一点。
最后 - C++17 标准怎么说?
10.1.5 constexpr 说明符 [dcl.constexpr]
- “......如果不存在任何参数值,使得函数或构造函数的调用可以是核心常量表达式(8.20)的评估子表达式,......,程序是错误的,不需要诊断。
接下来我们看看什么是“核心常量表达式”:
8.20 常量表达式 [expr.const]
- 表达式是对的求值,遵循抽象机 (4.6) 的规则,将求值以下表达式之一:
2.22 - 抛出表达式 (8.17)
并注意“不需要诊断”并且编译器不需要提供失败原因的详细解释。