【发布时间】:2019-06-08 10:59:33
【问题描述】:
为什么 constexpr 函数在编译时不求值,而在运行时在 main 函数的 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
使用 gcc 8.2。但是当我在return语句中调用函数时
template<int x>
constexpr int fac() {
return fac<x - 1>() * x;
}
template<>
constexpr int fac<1>() {
return 1;
}
int main() {
return fac<3>();
}
我明白了
int fac<1>():
push rbp
mov rbp, rsp
mov eax, 1
pop rbp
ret
main:
push rbp
mov rbp, rsp
call int fac<3>()
nop
pop rbp
ret
int fac<2>():
push rbp
mov rbp, rsp
call int fac<1>()
add eax, eax
pop rbp
ret
int fac<3>():
push rbp
mov rbp, rsp
call int fac<2>()
mov edx, eax
mov eax, edx
add eax, eax
add eax, edx
pop rbp
ret
为什么第一个代码在编译时评估,第二个在运行时评估?
我还尝试了带有 clang 7.0.0 的 sn-ps 并在运行时对其进行评估。为什么这对 clang 来说是无效的 constexpr?
所有评估均在 Godbolt 编译器资源管理器中完成。
【问题讨论】:
-
您应该使用
-O3以获得最佳行为,没有它,这个问题毫无意义。正如发布的那样,问题实际上是关于“为什么没有优化的编译器会做这件事”,答案通常是“使调试更容易” -
这两个编译器在启用优化的情况下完全符合您的预期:godbolt.org/z/cCbFDX
-
constexpr的存在是为了让您编写自然代码以在需要它们的上下文中生成常量表达式。这不是对编译器的“优化这个总是”的提示。最好不要混淆它。 -
@StoryTeller 这条评论帮助了我上千本书。我认为你应该把它作为一个答案。我一直认为
constexpr是告诉编译器“这应该在编译时评估”,而我现在明白它是“这必须在编译时评估”。突然之间,关于它是否在编译时被评估的困惑感觉不那么严重了。 -
值得一提的是,在这个特定的示例中,不需要将
x作为模板参数传递。一个“普通”参数在这里就足够了。因此,您可以使用constexpr函数作为更易读的替代一些旧式元编程模板代码,这些代码以前必须用于生成编译时整数。