为什么在编译时不对constexpr函数求值,而在运行时在主函数的return语句中求值?
它尝试了
ItemsSource
结果是
<toolkit:AutoCompleteBox x:Name="boxbox" Height="23"
ItemsSource="{Binding surname}"
SelectedItem="{Binding surname, Mode=TwoWay}" Margin="93,38,119,95" />
使用gcc 8.2。但是当我在return语句
中调用该函数时template<int x>
constexpr int fac() {
return fac<x - 1>() * x;
}
template<>
constexpr int fac<1>() {
return 1;
}
int main() {
const int x = fac<3>();
return x;
}
我知道
main:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 6
mov eax, 6
pop rbp
ret
为什么在编译时评估第一个代码,而在运行时评估第二个代码?
我还尝试了clang 7.0.0的两个代码片段,并在运行时对其进行了评估。为什么这对于clang无效的constexpr?
所有评估均在Godbolt编译器资源管理器中完成。
答案 0 :(得分:26)
关于constexpr
的一个常见误解是“这将在编译时进行评估” 1 。
不是。引入constexpr
是为了让我们编写自然的代码,这些代码可以在需要它们的上下文中生成常量表达式。这表示“这必须在编译时才能评估” ,这是编译器将检查的内容。
因此,如果您编写了一个返回整数的constexpr
函数,则可以使用它来计算模板参数,constexpr
变量的初始化程序(如果是整数类型,则为const
)或数组大小。您可以使用该函数获取自然的,说明性的,可读的代码,而不用使用过去需要使用的旧元编程技巧。
但是constexpr
函数仍然是常规函数。 constexpr
说明符并不意味着编译器拥有 2 可以对其进行优化以使其在编译时进行折叠并进行恒定折叠。最好不要将其混淆为这样的提示。
1 -感谢user463035818的措词。
2 -c++20和consteval
是另外一个故事:)
答案 1 :(得分:12)
StoryTeller的回答很好,但是我认为可能会有一点不同。
使用constexpr
,可以区分三种情况:
在编译时上下文中需要结果,例如数组大小。在这种情况下,也必须在编译时知道参数。评估是在编译时可能,并且至少所有可诊断的错误都将在编译时发现。
仅在运行时知道参数,而在编译时不需要结果。在这种情况下,评估必须在运行时进行。
这些参数可能在编译时可用,但仅在运行时需要结果。
第四个组合(参数仅在运行时可用,编译时需要结果)是错误;编译器将拒绝此类代码。
现在,在情况1和3中,由于所有输入均可用,因此计算可以在编译时进行。但是为了方便第2种情况,编译器必须能够创建运行时版本,并且在可能的情况下,它可能会决定在其他情况下也使用此变体。
例如一些编译器内部支持可变大小的数组,因此即使该语言需要编译时数组范围,实现也可能会决定不这样做。