首页 > 解决方案 > return 语句中的 C++ constexpr 函数

问题描述

为什么 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 尝试了这两个片段,并在运行时对其进行评估。为什么这对 clang 无效 constexpr?

所有评估都是在 Godbolt 编译器资源管理器中完成的。

标签: c++gccclangconstexpr

解决方案


一个常见的误解constexpr是它的意思是“这将在编译时进行评估” 1

它不是。constexpr引入的目的是让我们编写可以在需要它们的上下文中生成常量表达式的自然代码。这意味着“这必须在编译时可评估”,这是编译器将检查的内容。

因此,如果您编写了一个constexpr返回 int 的函数,您可以使用它来计算模板参数、变量的初始constexpr值设定项(const如果它是整数类型)或数组大小。您可以使用该函数来获得自然的、声明性的、可读的代码,而不是过去需要求助于旧的元编程技巧。

但是constexpr函数仍然是常规函数。说明constexpr符并不意味着编译器2来优化它以在编译时进行常量折叠。最好不要因为这样的提示而混淆它。


1 - 感谢user463035818的措辞。
2 - consteval但是是一个不同的故事:)


推荐阅读