从另一个 constexpr 内部调用 Constexpr

Tar*_*ets 13 c++ language-lawyer constexpr

我知道,这些问题很早就被问到了(例如constexpr 函数中的非 constexpr 调用),但让我们看下一个代码:

\n
consteval int factorial(int n)\n{\n    return n <= 1 ? 1 : (n * factorial(n - 1));\n}\n\nfactorial(5);\n
Run Code Online (Sandbox Code Playgroud)\n

一切都好。我们保证,该factorial(5)表达式在编译时得到解析,因为consteval. 正确的?如果是这样,我认为这应该意味着factorial(n - 1)调用 Factorial(5) 中的递归也在编译时解决。然而,我们也知道,声明中的int factorial(int n)参数int n只是一个变量,而不是constexpr。如果我们尝试做这样的事情,这会产生影响:

\n
consteval int factorial(int n)\n{\n  // 1 \n  constexpr auto res = factorial(n - 1); // error: \xe2\x80\x98n\xe2\x80\x99 is not a constant expression\n    \n  // 2\n  return n <= 1 ? 1 : (n * factorial(n - 1)); // hhhhmmmmmm...but all is ok.. \n}\n    \nfactorial(5);\n
Run Code Online (Sandbox Code Playgroud)\n

我们有什么?

\n
    \n
  1. 我们将常量称为 consteval 函数。好的。
  2. \n
  3. 在 consteval 函数中,我们在第 2 行使用非 constexpr 局部参数递归调用该函数。尽管我们使用非 constexpr 值调用 consteval 函数,但一切正常。好吧,我们可以建议,编译器知道,基址调用已作为正确的 consteval call 完成factorial(5),并且整个最终表达式(包含 的所有内部代码factorial)应解释为 consteval。是的?或者,为什么?因为...
  4. \n
  5. 在第 1 行,我们使用非 constexpr 值显式调用 constexpr。我们得到一个错误。
  6. \n
\n

我的问题是:为什么factorial(5)编译器的显式 consteval 调用会在阶乘的显式和隐式 constexpr 递归调用之间产生差异?这是错误还是功能?

\n

HTN*_*TNW 8

让我们回顾一下什么是常量表达式。核心常量表达式是一种在计算时不会导致一长串“不良”行为之一的表达式。常量表达式是一个核心常量表达式,其结果被一些其他规则“允许”(这里不重要)。特别要注意的是,这些条件在很大程度上是非语法的:常量表达式不是通过定义哪些表达式是常量表达式来积极定义的,而是通过定义常量表达式不能做什么来消极地定义。

此定义的结果是,即使表达式需要对许多非常量表达式(甚至非核心常量表达式)求值,它也可以是常量表达式。在定义中

consteval int factorial1(int n) {
  if(n == 0) return 1;
  else { // making this correct since undefined behavior interferes with constant expressions
    /*constexpr*/ auto rec = factorial1(n - 1);
    return n * rec;
  }
}
consteval int factorial2(int n) {
  return n == 0 ? 1 : n * factorial2(n - 1);
}
Run Code Online (Sandbox Code Playgroud)

in不是常量表达式,因此添加to会出错factorial1(n - 1)。同样,in也不是常量表达式。原因是相同的:这两个表达式都读取对象的值(执行左值到右值转换),该对象并未在表达式内启动生命周期。但这很好:/函数的主体不会被检查为常量表达式。真正要做就是将出现在常量表达式中的函数调用列入白名单而且,同样,即使您需要途中计算非常量表达式(例如 ),表达式也可以是常量(例如)。(在这种情况下,当评估 时,作为参数的对象的生命周期确实在被检查的表达式内开始其生命周期,因此可以在评估期间读取它。)factorial1constexprrecn == 0 ? 1 : n * factorial2(n - 1)factorial2nconstexprconstevalconstexpr factorial1(5)factorial(n - 1)factorial1(5)nfactorial

检查表达式是否为常量表达式的两个地方是constexpr变量的初始化和对函数的“非受保护”调用consteval。第一个解释了为什么添加constexprrecin是一个错误:您正在为在正确的函数中完成的factorial1常量表达式添加额外的检查,并且此额外的检查(正确地)失败。这应该已经回答了你的第3点。factorial1

consteval对于你的第二点:是的,对于从其他函数调用的函数有一个特殊的“保护” consteval。通常,对函数的调用consteval在编写时就被检查是否是常量表达式。正如我们一直在讨论的,对于上述定义中的调用factorial1(n - 1)和此检查将失败。语言中内置了一种特殊情况来保存它们:在直接函数上下文factorial2(n - 1)中调用函数(基本上,其直接封闭函数也是)不需要是常量表达式。consteval consteval