4

I am finding it a bit hard to understand why the following results in a compile time calculation. I have read this, this, this and a lot more questions on stackoverflow that tell me the following code( at least to my understanding) should not be computed at compile time because of the while loop (The code is just an example to illustrate the question):

template< unsigned N >
constexpr unsigned isStringNice(const char (&arr)[N], unsigned pos = 0)
{
    //we do not like the 'D' char :)
    int currPos = 0;
    while(currPos < N){
        if(arr [currPos] == 'D'){
            throw 1;
        }
        currPos ++;
    }
    return 1;
}
constexpr unsigned isIdxValid( unsigned idx, unsigned len){
    return idx >= len?  throw 1 : idx;
}

template< unsigned N >
constexpr char nth_char(const char (&arr)[N], unsigned pos){
  return  isStringNice(arr),isIdxValid(pos, N),arr[pos];
}

int main(){

  constexpr char b = nth_char("ABC", 2);

  return b;
}

this outputs with no flag the following assembly code (gcc 8.2 , thank you Godbolt) main:

push    rbp
mov     rbp, rsp
mov     BYTE PTR [rbp-1], 67
mov     eax, 67
pop     rbp
ret

and with -O3

main:
        mov     eax, 67
        ret

Notice as there is no jump in there, no branch on the while condition, nothing. I had the impression that for-loops and while loops were not possible to be evaluated at compile time. However, the compiler (gcc 8.2) evaluates the result at compile time. My only thought is this happens because of loop unrolling, so i tried using -fno-unroll-loops, but this results in the same assembly code.On the other hand from my experience, this flag is more like a compiler suggestion than a guarantee and gcc might still unroll the loop even though the flag is set.

Short version of my question: How come my while loop in a constexpr function is evaluated at compile time?

eucristian
  • 391
  • 3
  • 17
  • 8
    `constexpr` means precisely "evaluate at compile-time (when requested)". But we had to wait until C++14 to use loops in `constexpr` functions, hence the conflicting information (your links are for C++11). This is also completely orthogonal to what the optimizer does, as it operates under the *as-if* rule, and if there's anything it can do at compile-time it might as well do it. – Quentin Mar 20 '19 at 14:20
  • If you tried to compile this on a C++11 compiler, you'd get a compile error about the function not being allowed to be `constexpr` – Taekahn Mar 28 '19 at 09:30

1 Answers1

9

In C++14, constexpr function requirements were relaxed.

Previously, in C++11, constexpr functions could only contain typedefs, static_asserts and usings, but only a single return statement.

In C++14, it became possible to use loops in constexpr function bodies.

Because b was declared as constexpr char, it must be evaluated at compile time. The compiler then optimised out the isStringNice function, as it is not used at run time.

Artyer
  • 31,034
  • 3
  • 47
  • 75
  • I am not so sure about the "**must** be evaluated at compile time" in this context, I am not that experienced with `constexpr` but the only way to be sure it actually is evalauted at compile time I know is to use it as a template parameter – 463035818_is_not_an_ai Mar 20 '19 at 14:28
  • @user463035818 A constexpr variable must be initialised as a constant expression (i.e., able to be evalutated at compile time), and will itself be a constant expression. So it's value must be known at compile time. The "as-if" rule means that while the compiler could technically evaluate it at run time, it (should) have no effect on your program (apart from the assembly output). But there is no reason for the compiler to do that. – Artyer Mar 20 '19 at 14:41
  • after reading [here](https://en.cppreference.com/w/cpp/language/constexpr) again I still think that declaring `b` as `constexpr` alone does not necessarily require that it is evaluated at compile time, though I dont see any reason why the compiler shouldnt do it, and I still might misunderstand it – 463035818_is_not_an_ai Mar 20 '19 at 15:22
  • 1
    @user463035818: a constexpr constant can be used as a template parameter. I think this explains that there is no difference between constexpr and template parameter in this regard. – geza Mar 20 '19 at 15:47
  • @geza when used a template parameter then there is no doubt, but I am not sure about declaring the variable `constexpr` alone. Anyhow, sorry for nitpicking on something I am not sure myself, will have to do some reading and experimenting... – 463035818_is_not_an_ai Mar 20 '19 at 15:50