为什么在适用时不暗示 constexpr?
Why isn't constexpr implied when applicable?
这些可能在不同的问题中,但它们是相关的,所以......
-
为什么我们需要写
constexpr
?给定一组限制,编译器是否不能评估代码以查看它是否满足constexpr
要求,如果满足,则将其视为constexpr
?作为一个纯粹的文档关键字,我不确定它是否成立,因为我想不出我(其他人constexpr
函数的用户)应该真正关心它是否运行时的情况。这是我的逻辑:如果它是一个昂贵的函数,我认为作为一个好的做法,无论我是否给它编译时常量输入,我都应该这样对待它。这可能意味着在加载时调用它并保存结果,而不是在执行的关键点调用它。原因是
constexpr
实际上并不能向我保证它首先不会在运行时执行——所以也许一个新的/不同的机制应该这样做。 -
constexpr
限制似乎将许多(如果不是大多数)函数排除在编译时计算之外,这在逻辑上可能是可能的。我已经读到这至少部分(或者可能全部?)以防止无限循环和挂起编译器。但是,如果这是原因,它合法吗?
编译器不应该能够计算,对于任何给定的constexpr
函数,使用给定的输入,它是否无限循环?这并不能解决任何输入的停止问题。constexpr
函数的输入是编译时常数和有限的,因此编译器只需检查有限输入集的无限循环:实际使用的输入。如果您编写编译时无限循环,这应该是一个常规编译错误。
我问了一个非常相似的问题,为什么我们需要将函数标记为constexpr?
当我追问Clang的作者理查德·史密斯(Richard Smith)时,他解释说:
constexpr关键字确实有用。
它会影响函数模板专用化的时间(如果在未计算的上下文中调用 constexpr 函数模板专用化,则可能需要实例化它们;对于非 constexpr 函数来说也是如此,因为对 1 的调用永远不能成为常量表达式的一部分)。如果我们删除了关键字的含义,我们将不得不尽早实例化更多的专业化,以防调用恰好是一个常量表达式。
它通过限制实现在翻译期间尝试评估的函数调用集来减少编译时间。(这对于需要实现来尝试常量表达式计算的上下文很重要,但如果此类评估失败,则不是错误 - 特别是静态存储持续时间对象的初始值设定项。
起初这一切似乎并不令人信服,但如果你仔细研究细节,事情就会在没有constexpr
的情况下解开。函数在使用 ODR 之前不需要实例化,这实质上意味着在运行时使用。constexpr
函数的特殊之处在于,它们可能违反此规则并且无论如何都需要实例化。
函数实例化是一个递归过程。实例化函数会导致实例化它使用的函数和类,而不考虑任何特定调用的参数。
如果在实例化此依赖树时出现问题(可能会付出巨大的代价),则很难接受错误。此外,类模板实例化可能会产生运行时副作用。
给定函数签名中依赖于参数的编译时函数调用,重载解析可能会导致函数定义的实例化,这些函数定义仅辅助于重载集中的函数定义,包括甚至未被调用的函数。此类实例化可能会产生副作用,包括格式错误和运行时行为。
可以肯定的是,这是一个极端情况,但如果您不要求人们选择加入constexpr
功能,则可能会发生不好的事情。
对于constexpr
对象,某些类型可以生成核心常量表达式,这些表达式可在常量表达式上下文中使用,而无需声明constexpr
。但是,您并不真正希望编译器在编译时尝试计算每个表达式。这就是不断传播的目的。另一方面,在编译时记录何时需要发生某些事情似乎非常重要。
[注意,我完全改变了我的答案]
为了回答你的第二个问题,这里有两种情况的编译器:
-
编译器必须能够处理任意
constexpr
函数。在这种情况下,您仍然会遇到停止问题,因为输入集是constexpr
函数和对它们的调用的所有组合。 -
编译器可以处理一组有限的
constexpr
函数。在这种情况下,编译器实际上可以确定某些程序是否会导致无限循环,而其他程序将无法编译(因为它们不在有效输入集中)。
因此,据推测,这些限制已经到位,以便它满足情况 2 的合理编译器工作量。
这一决定背后既有技术原因,也有意识形态原因。
-
默认情况下,我们并不总是想要
constexpr
自己 - 它可能需要编译时间太长。这是第一。 想象一下你实现了isPrime
功能,您有 100 个大 constexpr 通话传入的值。我认为你(在大多数情况下)不想做编译器编译它的时间延长了几分钟,因为它决定在编译时本身需要这些值。但如果情况正是如此 - 手动指定constexpr
修饰符。这增加了下一点: -
向后兼容性 - 假设每个可能的 C++98 程序作者将此程序转换为 C++11 都想要
constexpr
是不明智的。 -
第二点是确定函数是否可以
constexpr
本身需要编译时间。如果它试图为每个可能的功能这样做,则需要一些额外的时间开销。甚至更多,通常是编译器根本无法确定给定的函数是否可以是 constexpr,所以你的第一个假设是不正确的。
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 多成员Constexpr结构初始化
- 条件constexpr函数
- constexpr 函数中的非文字(通过 std::is_constant_evaluated)
- Visual C++ constexpr Hints
- 如何确认我的constexpr表达式实际上已经在编译时执行
- 为什么constexpr的性能比正常表达式差
- 是否可以使用if constexpr删除控制流语句
- 要与"if constexpr"一起使用的编译时消息(在预处理器之后)
- 为什么std::isnan 不是 constexpr?
- Constexpr替代了新的放置方式,可以让内存中的对象保持未初始化状态
- 当一个值是非常量但用常量表达式初始化时使用constexpr
- 更多constexpr容器是否需要mark_immutable_if_consexpr
- C++从其他 constexpr 创建 lambda 不能按顺序执行 Constexpr
- constexpr上下文中std::initializer_list的验证
- constexpr构造函数需要常量成员函数时出现问题
- vs 2015 constexpr变量不恒定,但与2019相比还好吗
- 为什么在适用时不暗示 constexpr?
- constexpr是否暗示noexcept
- constexpr是否暗示内联?