据我了解,constexpr可以看作是编译器的一个提示,用于检查给定的表达式是否可以在编译时进行评估,如果可能的话也可以这样做。
我知道它也对声明为constexpr的函数或初始化施加了一些限制,但最终目标是编译时评估,不是吗?
所以我的问题是,为什么我们不能把它留在编译器?它显然能够检查前置条件,那么为什么不对每个表达式进行检查并在可能的情况下在编译时进行评估呢?
关于为什么会出现这种情况,我有两个想法,但我还不确定它们是否达到了这一点:
a)编译时可能需要很长时间。
b)由于我的代码可以在不允许使用normale函数的位置使用constexpr函数,因此说明符也是声明的一部分。如果编译器自己完成所有操作,可以在C数组定义中使用函数的一个版本,但是下一个版本可能存在编译器错误,因为编译时评估的前提条件是否定的更满意。
答案 0 :(得分:2)
这只是我的评价,但我相信你的(b)理由是正确的(它构成了编译器可以强制执行的接口的一部分)。接口要求既适用于代码的编写者,也适用于代码的客户端。
编写者可能打算在编译时上下文中使用某些东西,但实际上并不是以这种方式使用它。如果作者违反了constexpr
的规则,他们可能会在发布后发现尝试使用它constexpr
的客户失败。或者,更现实地,库可能在版本1中使用constexpr
意义上的代码,在版本2中重构此用法,并在版本3中打破constexpr
兼容性而没有意识到它。通过检查constexpr
- 合规性,将在部署之前捕获版本3中的破损。
客户端的界面更加明显---内联函数不会默默地变为constexpr
- 必需,因为它恰好工作而且有人用这种方式。
我不相信你的(a)理由(编译器可能需要很长时间)是适用的,因为(1)当代码被标记时,编译器必须检查大部分constexpr
约束,(2)没有注释,编译器只需要以constexpr
方式进行检查(因此大多数函数都不会 进行检查),并且(3) )IIUC D 编程语言实际上允许函数在没有任何声明帮助的情况下满足要求时进行编译时评估,因此显然可以完成。
答案 1 :(得分:2)
constexpr
不是编译器对任何事情的“暗示”; constexpr
是要求。它不需要在编译时实际执行表达式;它要求可以。
constexpr
(对于函数)的作用是限制允许放入函数定义的内容,以便编译器可以在编译时尽可能轻松地执行该代码。这是程序员和编译器之间的契约。如果您的函数违反了合同,编译器将立即出错。
一旦建立了合同,您现在可以在语言需要编译时常量表达式的地方使用这些constexpr
函数。然后,编译器可以检查常量表达式的元素,以查看表达式constexpr
函数中的所有函数调用;如果不这样做,则会再次产生编译错误。
您尝试隐藏此操作会导致两个问题。首先,如果没有语言定义的明确合同,我怎么知道constexpr
函数中我能做什么和不能做什么?我怎么知道什么会使函数不是constexpr
?
第二,没有合同在编译器中,通过声明我的意图使函数constexpr
,编译器如何能够验证我的函数是否符合该合同?它不能;它必须等到我在常量表达式中使用它才能发现它实际上不是一个正确的constexpr
函数。
最好明确地预先说明合同。
答案 2 :(得分:1)
constexpr可以看作是编译器的一个提示,用于检查给定的表达式是否可以在编译时进行评估,并在可能的情况下进行评估
不,见下文
最终目标是编译时评估
不,见下文。
那么为什么不对每个表达式做,并在可能的情况下在编译时进行评估?
优化器会按照as-if规则执行此类操作。
constexpr
不用于使事情更快,它用于允许在运行时变量表达式非法的上下文中使用结果。
答案 3 :(得分:0)
我想我记得看过 Bjarne Stroustrup 的早期演讲,他提到程序员希望对这个“危险”功能进行细粒度控制,从中我了解到他们不希望在没有它们的情况下在编译时“意外”执行事情会心。 (即使这听起来是件好事。)
这可能有很多原因,但我认为唯一有效的原因是最终编译速度((a)在您的列表中)。 确定每个函数是否可以在编译时计算对编译器来说是太大的负担。 随着编译时间总体上的下降,这个论点变得更弱了。
像 C++ 的许多其他特性一样,最终发生的是我们最终得到了“错误的默认值”。
所以你必须告诉你什么时候想要 constexpr
而不是什么时候你不想要 constexpr
(runtimeexpr
);你必须告诉你什么时候想要const
而不是你想要mutable
等等。
诚然,您可以想象一些函数在编译时运行需要大量时间,并且在运行时无法摊销(使用其他类型的机器资源)。
(我不知道“超时”可能是 constexpr
编译器中的一个标准,但可能是这样。)
或者可能是在一个总是期望在有限时间内完成编译但允许(或可调试)无界运行时的系统中进行编译。
我知道这个问题很老了,但时间已经表明,将 constexpr
设为默认值实际上是有意义的:
例如,在 C++17 中,您可以声明一个 lambda constexpr
,但更重要的是,如果它们可以,则默认情况下它们是 constexpr
。
https://docs.microsoft.com/en-us/cpp/cpp/lambda-expressions-constexpr
请注意,lambda 具有所有“正确”(相反)默认值,成员(捕获)默认为 const
,参数默认为模板 auto
,现在这些函数为 {{1} } 默认情况下。