首页 > 解决方案 > 编译时计算 (C++ v. C)

问题描述

我知道该constexpr关键字可用于在 C++ 中执行编译时计算。例如:

constexpr int factorial(int n)
{
    return n <= 1 ? 1 : (n * factorial(n - 1));
}

(取自https://en.cppreference.com/w/cpp/language/constexpr

编译时计算可以被认为是 C++ v. C 的一个关键优势吗?

据我了解,编译时计算在 Cconstexpr中是不可能的。不可用,我相信代码必须在运行时进行评估。

与等效的 C 程序相比,这是 C++ 程序可以实现更好性能(例如速度)的一种方式吗?

标签: c++cmetaprogrammingconstexprcompile-time

解决方案


只有一件事是确定的——编译时计算使得 C++ 编译器必然更复杂编译速度必然更慢,因为编译时需要编译器来做;例如见

constexpr int factorial(int n) {
    return n <= 1 ? 1 : (n * factorial(n - 1));
}

int main(void) {
    static_assert(factorial(10) == 3628800, "factorial 10 was correct");
    static_assert(factorial(3) == 42, "factorial 3 was 42");
}

由于后者而不是前者,它必须无法编译。 static_assert


AC 编译器不需要这种复杂性,因为没有要求 C 编译器必须能够在编译期间计算递归函数的值。一个简单的 C 编译器可以很好地将每个语句分别组装成机器代码,而不必记住前面的语句做了什么。C 标准当然不要求它能够在编译期间评估递归函数。

但这并不是说没有 C 编译器会在编译期间这样做。看这个例子:

#include <stdio.h>

int factorial(int n) {
    return n <= 1 ? 1 : (n * factorial(n - 1));
}

int main(void) {
    printf("%d\n", factorial(10));
}

使用 GCC 10.2 编译为带有 -O3C 程序,并且由于as-if规则,程序变成

factorial:
        mov     eax, 1
        cmp     edi, 1
        jle     .L4
.L3:
        mov     edx, edi
        sub     edi, 1
        imul    eax, edx
        cmp     edi, 1
        jne     .L3
        ret
.L4:
        ret
.LC0:
        .string "%d\n"
main:
        sub     rsp, 8
        mov     esi, 3628800
        mov     edi, OFFSET FLAT:.LC0
        xor     eax, eax
        call    printf
        xor     eax, eax
        add     rsp, 8
        ret

哪个更直接对应

unsigned factorial(unsigned n) {
     unsigned i = 1;
     while (n > 1) {
         i *= n;
         n --;
     }
     return i;
}

int main(void) {
    printf("%d\n", 3628800);
}

即编译器不仅将递归展平为一个简单的while循环,而且还解析了常量的阶乘,而且都没有任何特殊的关键字。


推荐阅读