在 return 值未被使用的情况下,g++ 编译器是否将 constexpr 函数视为常规函数?

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

我试图看到一个由g++编译的cpp constexpr函数的编译代码。 我看到如果函数不是 return 什么都不是,编译器会将其视为常规函数,但如果它 return 是一个值并且我将此值分配给 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 变量),编译器 必须 在编译时计算函数.

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

引自cppreference(由我突出显示):

The constexpr specifier declares that it is possible to evaluate the value of the function or variable at compile time. Such variables and functions can then be used where only compile time constant expressions are allowed (provided that appropriate function arguments are given).

如果你用例如编译你的第一个例子-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    4, -4(%rbp)   #, pi2
 # temp.cpp:13:  cout<<pi3<<endl;
        movl    4, %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    [=11=], %eax         #, _7
        addq    , %rsp        #,
        popq    %rbp     #
        ret

因此,gcc 可以在编译时计算 constexpr 函数。

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

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

  • 编写代码生成代码(如上面示例中计算pi),
  • 编译并运行该代码,
  • 用其余代码编译生成的代码。

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