是否允许编译器在运行时调用立即(consteval)函数?

Is compiler allowed to call an immediate (consteval) function during runtime?

这可能是个愚蠢的问题,但我很困惑。我有一种感觉,即刻 (consteval) 函数 has 在编译期间执行,我们根本看不到它在二进制文件中的主体。

This article 明显支持我的感觉:

This has the implication that the [immediate] function is only seen at compile time. Symbols are not emitted for the function, you cannot take the address of such a function, and tools such as debuggers will not be able to show them. In this matter, immediate functions are similar to macros.

可能在 Herb Sutter's publication:

中找到类似的强烈声明

Note that draft C++20 already contains part of the first round of reflection-related work to land in the standard: consteval functions that are guaranteed to run at compile time, which came from the reflection work and are designed specifically to be used to manipulate reflection information.

然而,有许多证据并不能说明这一事实。

来自 cppreference:

consteval - specifies that a function is an immediate function, that is, every call to the function must produce a compile-time constant.

这并不意味着它必须只能在编译时调用。

来自 the P1073R3 proposal:

There is now general agreement that future language support for reflection should use constexpr functions, but since "reflection functions" typically have to be evaluated at compile time, they will in fact likely be immediate functions.

好像是我想的意思,但是还是没说清楚。来自同一个提案:

Sometimes, however, we want to express that a function should always produce a constant when called (directly or indirectly), and a non-constant result should produce an error.

同样,这并不意味着函数必须只在编译时求值。

来自 :

your code must produce a compile time constant expression. But a compile time constant expression is not an observable property in the context where you used it, and there are no side effects to doing it at link or even run time! And under as-if there is nothing preventing that

最后,there is a live demo, where consteval function is clearly called during runtime. However, I hope this is due to the fact consteval is not yet properly supported in clang and the behavior is actually incorrect, just like in

更准确地说,我想听听引用文章的以下哪些陈述是正确的:

  1. 立即函数仅在编译时可见(并且无法在 运行 时求值)
  2. 不会为即时函数发出符号
  3. 调试器等工具将无法显示即时功能

提案提到:

One consequence of this specification is that an immediate function never needs to be seen by a back end.

所以用常量代替调用绝对是提议的意图。换句话说,常量表达式在翻译过程中被求值。

但是,并没有说要求后台看不到。其实在proposal的另外一句话里,只是说不太可能:

It also means that, unlike plain constexpr functions, consteval functions are unlikely to show up in symbolic debuggers.


更一般地说,我们可以将问题重述为:

Are compilers forced to evaluate constant expressions (everywhere; not just when they definitely need it)?

例如,如果常量表达式是数组元素的数量,编译器就需要对其求值,因为它需要静态确定数组的总大小。

但是,编译器可能不需要评估其他用途,虽然任何体面的优化编译器都会尝试这样做,但这并不意味着它需要。

另一个值得思考的有趣案例是解释器:虽然解释器仍然需要计算一些常量表达式,但它可能一直懒洋洋地做,而不执行任何常量折叠。

因此,据我所知,它们不是必需的,但我不知道我们需要标准中的确切引述来证明它(或以其他方式)。也许它本身就是一个很好的后续问题,它也可以回答这个问题。

例如,在 [expr.const]p1 中有一条注释说他们可以,而不是说他们是:

[Note: Constant expressions can be evaluated during translation. — end note]

To be more precise, I'd like to hear which of the following statements of the cited article are correct:

  1. An immediate function is only seen at compile time (and cannot be evaluated at run time)
  2. Symbols are not emitted for an immediate function
  3. Tools such as debuggers will not be able to show an immediate function

其中几乎none是C++标准可以给出的答案。该标准没有定义“符号”或工具可以显示的内容。就标准而言,这些几乎都是经销商的选择。

的确,即使是“编译时间”与“运行 时间”的问题,标准也没有涉及。与标准有关的唯一问题是某物是否是常量表达式。调用 constexpr 函数 可能 产生常量表达式,具体取决于其参数。以不产生常量表达式的方式调用 consteval 函数是错误格式的。

标准 做的一件事 定义的是什么被“看到”。虽然它不是真正关于“编译时间”。 C++20 中有许多语句禁止大多数函数将 pointers/references 处理为立即数函数。例如,C++20 在 [expr.prim.id]/3 中声明:

An id-expression that denotes an immediate function shall appear only

  • as a subexpression of an immediate invocation, or

  • in an immediate function context.

因此,如果您不在立即函数中,或者您没有使用立即函数的名称来调用另一个立即函数(将 pointer/reference 传递给该函数),则您无法命名立即函数。而且你不能在不命名的情况下为函数获取 pointer/reference。

这个和规范中的其他语句(如 pointers to immediate function not being valid results of constant expressions)基本上使得 pointer/reference 到立即函数不可能泄漏到常量表达式之外。

所以关于立即函数可见性的说法在某种程度上是正确的。符号 可以 为立即函数发出,但是您不能以 阻止 丢弃所述符号的实现的方式使用立即函数。

consteval 基本上就是这样。它不使用标准语言来强制执行必须发生的事情。它使用标准语言使得无法以防止这些事情发生的方式使用函数。所以更合理的说法是:

  1. 您不能以阻止编译器在编译时执行它的方式使用立即函数。

  2. 您不能以阻止编译器丢弃其符号的方式使用立即函数。

  3. 您不能以强制调试器能够看到它们的方式使用即时函数。

预计实施质量将从那里开始。

还应注意调试构建是为了...调试。高级编译器工具能够调试生成常量表达式的代码是完全合理的。因此,可以看到即时函数执行的调试器是一项完全可取的技术。随着编译时代码变得越来越复杂,这种情况变得更加严重。