为什么在适用时不暗示 constexpr?

Why isn't constexpr implied when applicable?

本文关键字:暗示 constexpr 为什么      更新时间:2023-10-16

这些可能在不同的问题中,但它们是相关的,所以......

  1. 为什么我们需要写constexpr?给定一组限制,编译器是否不能评估代码以查看它是否满足constexpr要求,如果满足,则将其视为constexpr作为一个纯粹的文档关键字,我不确定它是否成立,因为我想不出我(其他人constexpr函数的用户)应该真正关心它是否运行时的情况。

    这是我的逻辑:如果它是一个昂贵的函数,我认为作为一个好的做法,无论我是否给它编译时常量输入,我都应该这样对待它。这可能意味着在加载时调用它并保存结果,而不是在执行的关键点调用它。原因是constexpr实际上并不能向我保证它首先不会在运行时执行——所以也许一个新的/不同的机制应该这样做。

  2. constexpr限制似乎将许多(如果不是大多数)函数排除在编译时计算之外,这在逻辑上可能是可能的。我已经读到这至少部分(或者可能全部?)以防止无限循环和挂起编译器。但是,如果这是原因,它合法吗?

编译器不应该能够计算,对于任何给定的constexpr函数,使用给定的输入,它是否无限循环?这并不能解决任何输入的停止问题。constexpr函数的输入是编译时常数和有限的,因此编译器只需检查有限输入集的无限循环:实际使用的输入。如果您编写编译时无限循环,这应该是一个常规编译错误。

我问了一个非常相似的问题,为什么我们需要将函数标记为constexpr?

当我追问Clang的作者理查德·史密斯(Richard Smith)时,他解释说:

constexpr关键字确实有用。

它会影响函数模板专用化

的时间(如果在未计算的上下文中调用 constexpr 函数模板专用化,则可能需要实例化它们;对于非 constexpr 函数来说也是如此,因为对 1 的调用永远不能成为常量表达式的一部分)。如果我们删除了关键字的含义,我们将不得不尽早实例化更多的专业化,以防调用恰好是一个常量表达式。

它通过限制实现在翻译期间尝试评估的函数调用集来减少编译时间。(这对于需要实现来尝试常量表达式计算的上下文很重要,但如果此类评估失败,则不是错误 - 特别是静态存储持续时间对象的初始值设定项。

起初这一切似乎并不令人信服,但如果你仔细研究细节,事情就会在没有constexpr的情况下解开。函数在使用 ODR 之前不需要实例化,这实质上意味着在运行时使用。constexpr函数的特殊之处在于,它们可能违反此规则并且无论如何都需要实例化。

函数实例化是一个递归过程。实例化函数会导致实例化它使用的函数和类,而不考虑任何特定调用的参数。

如果在实例化此依赖树时出现问题(可能会付出巨大的代价),则很难接受错误。此外,类模板实例化可能会产生运行时副作用。

给定函数签名中依赖于参数的编译时函数调用,重载解析可能会导致函数定义的实例化,这些函数定义仅辅助于重载集中的函数定义,包括甚至未被调用的函数。此类实例化可能会产生副作用,包括格式错误和运行时行为。

可以肯定的是,这是一个极端情况,但如果您不要求人们选择加入constexpr功能,则可能会发生不好的事情。

对于constexpr对象,某些类型可以生成核心常量表达式,这些表达式可在常量表达式上下文中使用,而无需声明constexpr。但是,您并不真正希望编译器在编译时尝试计算每个表达式。这就是不断传播的目的。另一方面,在编译时记录何时需要发生某些事情似乎非常重要。

[注意,我完全改变了我的答案]

为了回答你的第二个问题,这里有两种情况的编译器:

  1. 编译器必须能够处理任意constexpr函数。在这种情况下,您仍然会遇到停止问题,因为输入集是constexpr函数和对它们的调用的所有组合。

  2. 编译器可以处理一组有限的constexpr函数。在这种情况下,编译器实际上可以确定某些程序是否会导致无限循环,而其他程序将无法编译(因为它们不在有效输入集中)。

因此,据推测,这些限制已经到位,以便它满足情况 2 的合理编译器工作量。

这一决定背后既有技术原因,也有意识形态原因。

  • 默认情况下,我们并不总是想要constexpr自己 - 它可能需要编译时间太长。这是第一。 想象一下你实现了 isPrime功能,您有 100 个大 constexpr 通话传入的值。我认为你(在大多数情况下)不想做编译器编译它的时间延长了几分钟,因为它决定在编译时本身需要这些值。但如果情况正是如此 - 手动指定constexpr修饰符。这增加了下一点:

  • 向后兼容性 - 假设每个可能的 C++98 程序作者将此程序转换为 C++11 都想要constexpr是不明智的。

  • 第二点是确定函数是否可以constexpr本身需要编译时间。如果它试图为每个可能的功能这样做,则需要一些额外的时间开销。甚至更多,通常是编译器根本无法确定给定的函数是否可以是 constexpr,所以你的第一个假设是不正确的。