G++ 编译器是否在未使用返回值的情况下将 constexpr 函数视为常规函数?

does g++ compiler treats constexpr functions as regular in a case their return value isnt being used?

本文关键字:函数 constexpr 常规 情况下 是否 编译器 未使用 返回值 G++      更新时间:2023-10-16

我试图查看由g ++编译的cpp constexpr函数的编译代码。 我看到,如果函数不返回任何内容,编译器会将其视为常规函数,但如果它返回一个值并且我将此值分配给 constexpr 变量,则只有在编译时才会计算它。

代码示例:

constexpr int func(int x){
return x!=0 ? 1: throw "Error";
}
int main(){
func(2);
}

和编译器输出:

push    rbp
mov     rbp, rsp
mov     edi, 2
call    func(int)
mov     eax, 0
pop     rbp
ret

如您所见,它在运行时调用 func。相反,如果我将函数结果断言为 constexpr:

constexpr int func(int x){
return x!=0 ? 1: throw "Error";
}
int main(){
constexpr int x = func(2);
}

和编译器输出:

main:
push    rbp
mov     rbp, rsp
mov     DWORD PTR [rbp-4], 1
mov     eax, 0
pop     rbp
ret

有人可以解释为什么编译器在编译时而不是运行时需要此赋值来评估函数吗?

允许编译器决定是否在编译时或运行时计算 constexpr 函数。仅当函数在需要编译时常量表达式(例如使用结果初始化constexpr变量(的上下文中使用时,编译器才必须在编译时计算函数。

在您的第一个示例中,情况并非如此,并且您可能在调试模式下编译,该函数在运行时调用,就像其他所有函数一样。

引用cpp偏好(由我突出显示(:

constexpr 说明符声明可以在编译时计算函数或变量的值。然后,可以在只允许编译时常量表达式的情况下使用此类变量和函数(前提是给出了适当的函数参数(。

如果您编译您的第一个示例,例如-O3,您将看到函数调用已优化。

我编译了下面的代码,以了解 gcc 11.2 是否优化了 constexpr 函数:

#include <iostream>
using namespace std;

constexpr double pi(){
double a=0.;
for(int i=1;i<100000;i+=4)a+=1./i-1./(i+2);
return a*4;
}

int main(){
constexpr int pi2=pi()*100;
enum {pi3=pi2};
cout<<pi3<<endl;
}

反汇编如下:

# temp.cpp:10: int main(){
call    __main   #
# temp.cpp:11:  constexpr int pi2=pi()*100;
movl    $314, -4(%rbp)   #, pi2
# temp.cpp:13:  cout<<pi3<<endl;
movl    $314, %edx       #,
movq    .refptr._ZSt4cout(%rip), %rax    #, tmp85
movq    %rax, %rcx       # tmp85,
call    _ZNSolsEi        #
movq    %rax, %rcx       #, _1
# temp.cpp:13:  cout<<pi3<<endl;
movq    .refptr._ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(%rip), %rax   #, tmp86
movq    %rax, %rdx       # tmp86,
call    _ZNSolsEPFRSoS_E         #
# temp.cpp:14: }
movl    $0, %eax         #, _7
addq    $48, %rsp        #,
popq    %rbp     #
ret

因此,gcc 可以在编译时评估 constexpr 函数。

如果需要更长的计算时间,则应增加 gcc 标志-fconstexpr-*。但我观察到编译时计算比(-O3标志(执行时间计算花费更多的时间。

如果需要更长的计算,我建议,

  • 编写代码以生成代码(如上面示例中的计算pi(,
  • 编译并运行该代码,
  • 将生成的代码与其余代码一起编译。

编写makefile来执行上述步骤会有所帮助。