编译时计算 (C++ v. C)

tha*_*ude 7 c c++ metaprogramming compile-time constexpr

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

constexpr int factorial(int n)
{
    return n <= 1 ? 1 : (n * factorial(n - 1));
}
Run Code Online (Sandbox Code Playgroud)

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

能否将编译时计算视为 C++ 与 C 的主要优势?

据我了解,在 C 中不可能进行编译时计算。constexpr不可用,我认为必须在运行时评估代码。

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

Ant*_*ala 6

只有一件事是确定的 - 编译时计算使 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");
}
Run Code Online (Sandbox Code Playgroud)

由于后者而不是前者,因此必须无法编译 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));
}
Run Code Online (Sandbox Code Playgroud)

与GCC 10.2作为编译C程序与-O3,并感谢AS-如果规则,程序成为

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
Run Code Online (Sandbox Code Playgroud)

哪个更直接对应

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

int main(void) {
    printf("%d\n", 3628800);
}
Run Code Online (Sandbox Code Playgroud)

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