代码之家  ›  专栏  ›  技术社区  ›  Thomas Sablik

返回语句中的C++ CONTHEXPR函数

  •  20
  • Thomas Sablik  · 技术社区  · 7 年前

    为什么constexpr函数不是在编译时计算的,而是在运行时在main函数的返回语句中计算的?

    它试过了

    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。但是当我调用返回语句中的函数时

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

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

    2 回复  |  直到 7 年前
        1
  •  24
  •   StoryTeller - Unslander Monica    7 年前

    关于 constexpr 这意味着 “这将在编译时进行评估” 一 .

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

    所以如果你写了 常量表达式 函数返回一个int,可以使用它计算模板参数,该参数是 常量表达式 变量(也) const 如果是整数类型)或数组大小。您可以使用该函数获得自然的、声明性的、可读的代码,而不是以前需要使用的元编程技巧。

    但一 常量表达式 函数仍然是一个正则函数。这个 常量表达式 说明符不表示编译器 有 二 在编译时对其进行优化,以检查并进行持续的折叠。最好不要因为这样的暗示而混淆它。


    一 -谢谢 user463035818 为了措辞。
    二 - 和 consteval 不过情况不同:)

        2
  •  12
  •   MSalters    7 年前

    讲故事的人的回答很好,但我认为有一个稍微不同的看法是可能的。

    用 constexpr ,有三种情况需要区分:

    1. 结果在编译时上下文中是必需的,例如数组大小。在这种情况下,参数也必须在编译时知道。评价是 可能 在编译时,至少会在编译时找到所有可诊断的错误。

    2. 这些参数只在运行时知道,编译时不需要结果。在这种情况下,评估必须在运行时进行。

    3. 参数可能在编译时可用,但结果只在运行时需要。

    第四个组合(参数仅在运行时可用,结果在编译时需要)是一个错误;编译器将拒绝此类代码。

    现在,在案例1和3中,计算可能在编译时进行,因为所有的输入都是可用的。但是为了方便案例2,编译器必须能够创建一个运行时版本,如果可以的话,它也可以决定在其他案例中使用这个变体。

    例如,有些编译器内部支持可变大小的数组,因此即使语言需要编译时数组边界,实现也可能决定不支持。