9

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

这篇文章显然支持了我的感觉:

这意味着 [immediate] 函数仅在编译时可见。该函数不会发出符号,您无法获取此类函数的地址,并且调试器等工具将无法显示它们。在这个问题上,立即函数类似于宏。

在Herb Sutter 的出版物中可以找到类似的强烈主张:

请注意,C++20 草案已经包含了第一轮反射相关工作的一部分,这些工作将被纳入标准:保证在编译时运行的consteval 函数,它来自反射工作,专门设计用于操纵反射信息。

然而,有许多证据对这一事实并不十分清楚。

cppreference

consteval - 指定一个函数是一个立即函数,也就是说,对函数的每次调用都必须产生一个编译时常量。

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

来自P1073R3 提案

现在普遍认为,未来语言对反射的支持应该使用 constexpr 函数,但由于“反射函数”通常必须在编译时进行评估,它们实际上很可能是即时函数。

似乎这意味着我的想法,但仍然没有说清楚。来自同一个提案:

然而,有时我们想表达一个函数在调用时(直接或间接)应该总是产生一个常量,而一个非常量的结果应该产生一个错误。

同样,这并不意味着函数必须仅在编译时进行评估。

这个答案

您的代码必须生成编译时常量表达式。但是编译时常量表达式在您使用它的上下文中不是可观察的属性,并且在链接甚至运行时执行它没有副作用!好像没有什么可以阻止

最后,有一个现场演示,其中consteval函数在运行时被清楚地调用。但是,我希望这是因为consteval在 clang 中还没有得到适当的支持,并且行为实际上是不正确的,就像在为什么 consteval 函数允许未定义的行为?

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

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

2 回答 2

6

该提案提到:

该规范的一个结果是后端永远不需要看到即时函数。

因此,将调用替换为常量绝对是提案的意图。换句话说,常量表达式在翻译期间被评估。

但是,它并没有说它必须不被后端看到。事实上,在提案的另一句话中,它只是说不太可能:

这也意味着,与普通constexpr函数不同,consteval函数不太可能出现在符号调试器中。


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

编译器是否被迫评估常量表达式(无处不在;不仅仅是当他们确实需要它时)?

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

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

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

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

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

[注意:常量表达式可以在翻译过程中求值。——尾注]

于 2019-10-19T18:09:23.330 回答
6

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

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

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

事实上,即使是“编译时间”与“运行时间”的问题,标准也没有处理。与标准有关的唯一问题是某事物是否是一个常量表达式。调用constexpr函数可能会产生一个常量表达式,具体取决于它的参数。consteval以不产生常量表达式的方式调用函数是不合格式的。

标准确实定义的一件事是“看到”的内容。尽管它实际上与“编译时间”无关。C++20 中有许多语句禁止大多数函数处理对立即函数的指针/引用。例如,[expr.prim.id]/3中的 C++20 状态:

表示立即函数的 id 表达式应仅出现

  • 作为立即调用的子表达式,或

  • 在直接函数上下文中。

因此,如果您不在立即函数中,或者您没有使用立即函数的名称来调用另一个立即函数(传递指向该函数的指针/引用),那么您不能命名立即函数。如果不命名函数,您将无法获得指向函数的指针/引用。

规范中的这个和其他语句(如指向立即函数的指针不是常量表达式的有效结果)本质上使指向立即函数的指针/引用不可能泄漏到常量表达式之外。

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

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

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

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

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

预计实施质量将从那里取得进展。

还应该注意的是,调试版本是为了……调试。高级编译器工具能够调试生成常量表达式的代码是完全合理的。因此,可以看到立即执行功能的调试器是一种完全可取的技术。随着编译时代码变得更加复杂,这变得更加复杂。

于 2019-10-19T19:48:52.240 回答