只有一件事是确定的 - 编译时计算使得 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
,必须在编译时无法通过编译,而前者可以。
C语言编译器不需要如此复杂,因为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编译成C语言程序,使用-O3优化参数,并由于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
循环,还计算了常量的阶乘,而且全部都没有使用任何特殊的关键字。
constexpr
或consteval
并不意味着编译器不能在编译时进行计算,如果所有值都已知。使用关键字告诉编译器您的意图,以便编译器可以警告您是否犯了错误,以确保您的意图。 - t.nieseconstexpr
的存在也不能保证编译时计算。 - rustyx