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

时间:2019-10-19 17:28:46

标签: c++ language-lawyer c++20 consteval

这可能是一个愚蠢的问题,但是我很困惑。我有一个感觉,立即(consteval)函数必须在编译时执行,而我们根本看不到二进制代码中的主体。

This article显然支持我的感觉:

  

这意味着[immediate]函数仅在编译时可见。该函数不会发出符号,您不能使用该函数的地址,并且调试器之类的工具将无法显示它们。在这种情况下,立即函数类似于宏。

Herb Sutter's publication中可以找到类似的强项:

  

请注意,C ++ 20草案已经包含了第一轮与反射相关的工作,并将其纳入该标准:约束函数保证在编译时运行,这些函数来自反射工作,并且专门用于处理反射信息。

但是,有很多证据对此事实不太清楚。

来自cppreference

  

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

这并不意味着它仅在编译时被调用。

来自the P1073R3 proposal

  

现在已经普遍同意,将来对反射的语言支持应该使用constexpr函数,但是由于通常在编译时对“反射函数”进行评估,因此它们实际上很可能是立即函数。

看起来这就是我的想法,但仍然不清楚。来自同一提议:

  

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

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

来自this answer

  

您的代码必须产生一个编译时间常数表达式。但是,在您使用编译时常数表达式的上下文中,它不是可观察的属性,并且在链接或甚至运行时时也没有副作用!在没有什么可以阻止的情况下

最后是there is a live demo,其中在运行时明确调用了consteval函数。但是,我希望这是由于consteval尚未在clang中得到正确支持,并且该行为实际上是不正确的,就像Why does a consteval function allow undefined behavior?

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

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

2 个答案:

答案 0 :(得分:5)

该提案提到:

  

此规范的一个后果是,立即函数不必在后端看到。

因此提案的意图肯定是用常量代替了调用。换句话说,常量表达式是在翻译过程中求值的。

但是,它并不表示要求后端不可见。实际上,在提案的另一句话中,它只是说不太可能:

  

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


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

  

编译器是否被迫对常量表达式求值(在任何地方;不仅仅是在确实需要时)?

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

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

另一个值得考虑的有趣案例是解释器:尽管解释器仍需要评估某些常量表达式,但它可能一直懒惰地执行它,而无需执行任何常量折叠。

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

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

  

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

答案 1 :(得分:3)

  

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

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

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

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

标准所做的定义是“看到”的内容。尽管这与“编译时间”无关。当前的C ++ 20草案中有许多语句禁止大多数函数处理对立即函数的指针/引用。例如,N4835(C ++ 20的最新工作草案)在[expr.prim.id] / 3中声明:

  

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

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

  •   
  • 在立即函数上下文中。

  •   

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

规范中的此语句和其他语句(例如pointers to immediate function not being valid results of constant expressions)基本上使指向立即函数的指针/引用不可能泄漏到常量表达式之外。

因此,有关立即函数可见性的声明在某种程度上是正确的。可以为立即函数发出符号 ,但是您不能以防止丢弃该符号的方式使用立即函数。

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

  1. 不能使用立即函数来阻止编译器在编译时执行它。

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

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

实施质量有望从那里得到好处。

还应注意,调试版本用于...调试。高级编译器工具能够调试生成常量表达式的代码是完全合理的。因此,可以看到立即执行功能的调试器是一项完全理想的技术。随着编译时代码变得越来越复杂,这一点变得越来越重要。