1

I learnt that constexpr functions are evaluated at compile time. But look at this example:

constexpr int fac(int n)
{
    return (n>1) ? n*fac(n-1) : 1;
}


int main()
{
    const int a = 500000;
    cout << fac(a);
    return 0;
}

Apparently this code would throw an error, but since constexpr functions are evaluated at compiling time, why I see no error when compile and link?

Further on, I disassembled this code, and it turned out this function isn't evaluated but rather called as a normal function:

(gdb) x/10i $pc
=> 0x80007ca <main()>:  sub    $0x8,%rsp
   0x80007ce <main()+4>:        mov    $0x7a11f,%edi
   0x80007d3 <main()+9>:        callq  0x8000823 <fac(int)>
   0x80007d8 <main()+14>:       imul   $0x7a120,%eax,%esi
   0x80007de <main()+20>:       lea    0x20083b(%rip),%rdi        # 0x8201020 <_ZSt4cout@@GLIBCXX_3.4>
   0x80007e5 <main()+27>:       callq  0x80006a0 <_ZNSolsEi@plt>
   0x80007ea <main()+32>:       mov    $0x0,%eax
   0x80007ef <main()+37>:       add    $0x8,%rsp
   0x80007f3 <main()+41>:       retq

However, if I call like fac(5):

constexpr int fac(int n)
{
    return (n>1) ? n*fac(n-1) : 1;
}


int main()
{
    const int a = 5;
    cout << fac(a);
    return 0;
}

The assemble code turned into:

(gdb) x/10i $pc
=> 0x80007ca <main()>:  sub    $0x8,%rsp
   0x80007ce <main()+4>:        mov    $0x78,%esi
   0x80007d3 <main()+9>:        lea    0x200846(%rip),%rdi        # 0x8201020 <_ZSt4cout@@GLIBCXX_3.4>
   0x80007da <main()+16>:       callq  0x80006a0 <_ZNSolsEi@plt>
   0x80007df <main()+21>:       mov    $0x0,%eax
   0x80007e4 <main()+26>:       add    $0x8,%rsp
   0x80007e8 <main()+30>:       retq

The fac function is evaluated at compile time.

Can Anyone explain this?

Compiling command:

g++ -Wall test.cpp -g -O1 -o test

And with g++ version 7.4.0, gdb version 8.1.0

andreee
  • 4,459
  • 22
  • 42
JiangFeng
  • 355
  • 2
  • 14

2 Answers2

4

I learnt that constexpr functions are evaluated at compile time

No, constexpr can be evaluated at compile time, but also at runtime.

further reading:

Apparently this code would throw an error

No, no errors thrown. For large input, the result will overflow which is undefined behavior. This doesn't mean an error will be thrown or displayed. It means anything can happen. And when I say anything, I do mean anything. The program can crash, hang, appear to work with a strange results, display weird characters, or literally anything.

further reading:

And, as pointed out by nathanoliver

when invoked in a constant expression, a constexpr function must check and error out on UB http://coliru.stacked-crooked.com/a/43ccf2039dc511d5

In other words there can't be any UB at compile time. What at runtime would be UB, at compile time it's a hard error.

bolov
  • 72,283
  • 15
  • 145
  • 224
  • Do note that when invoked in a constant expression, a `constexpr` function must check and error out on UB. This is not exactly the same as the OP's code since thier code can be called at run time but here is an example: http://coliru.stacked-crooked.com/a/43ccf2039dc511d5 – NathanOliver Aug 19 '19 at 12:41
3

constexpr it means that it can be evaluated at compile time, not that it will be evaluated at compile time. The compiler will be forced to do the evaluation compile-time if you use it where a compile time constant is expected (e.g. the size of an array).

On the other hand for small values g++ is for example smart enough to compute the result compile time (even without constexpr).

For example with:

int fact(int n) {
    return n < 2 ? 1 : n*fact(n-1);
}

int bar() {
    return fact(5);
}

the code generated by g++ -O3 for bar is:

bar():
    mov     eax, 120
    ret

Note that overflowing the call stack (e.g. infinite or excessive recursion) or even overflowing signed integer arithmetic is in C++ undefined behavior and anything can happen. It doesn't mean you'll get a nice "error" or even a segfault... but that ANYTHING can happen (including, unfortunately, nothing evident). Basically it means that the authors of compilers can just ignore to handle those cases because you're not supposed to do this kind of mistakes.

6502
  • 112,025
  • 15
  • 165
  • 265