3

Here is my code and I need clarification on what's happening:

constexpr int funct(int x){
    return x + 1;
}


int main(){
    int x = funct(10);
    return 0;
}

constexpr's allows compile time calculation, and based on my code above, since funct is declared as constexpr, it is allowed to do compile time calculations if the arguments are constants or constexpr's themselves.

The part that I am confused with lies in this part, the int x. Since it is not declared as constexpr, would it mean that int x will get the value at runtime? Does that mean that declaring it to be constexpr int x will mean that int x will get the value at compile time unlike int x ?

Vality
  • 6,577
  • 3
  • 27
  • 48
Carlos Miguel Colanta
  • 2,685
  • 3
  • 31
  • 49
  • 1
    What difference would it make? Or, how would you be able to even tell the difference? `x` is a local variable, it *has* to be initialised at runtime. – Greg Hewgill Jul 12 '15 at 23:51
  • 1
    Assuming no optimization, yes the declaration is evaluated at run time. However, a compiler is allowed to optimize this program down to nothing, since it does nothing. – Cheers and hth. - Alf Jul 12 '15 at 23:51
  • Ahhh i see. So if it's `int x` then it will surely be at runtime and `constexpr` will be at compile time ? – Carlos Miguel Colanta Jul 12 '15 at 23:52
  • @CarloBrew No. A `constexpr` function can still be used as normal function with runtime-dependent arguments. See eg. http://ideone.com/3fldYy . `constexpr` means that for a certain input, the output is everytime the same (and tehre are some restrictions on the function content), and with inputs known at compile-time, this can enable some optimizations etc.. But, as said, nothing stops you from passing stuff not known at compile time. – deviantfan Jul 12 '15 at 23:58
  • I think [Computing length of a C string at compile time. Is this really a constexpr?](http://stackoverflow.com/q/25890784/1708801) covers this. – Shafik Yaghmour Jul 13 '15 at 14:14

1 Answers1

4

It depends on the compiler in question on many counts. What sorts of optimizations that may take place, etc. However, constexpr does not inherently enable compile-time calculations.

Take this code:

#include <cstdio>

constexpr int test(int in)
{
   return in + 25;
}

int main(int argc, char* argv[])
{
   printf("Test: %u\n", test(5));
   printf("Two: %u\n", test(10));
}

Under GCC 4.8.4 on my x86_64 Gentoo box, that actually still makes a call on both counts to the supposedly compile-time "test". The lines I used were

g++ -std=c++11 -Wall -g  -c main.cpp -o obj/Debug/main.o
g++  -o bin/Debug/TestProject obj/Debug/main.o   

So on the code above, that produces the following machine code:

0x40061c    mov    edi,0x5
0x400621    call   0x400659 <test(int)>
0x400626    mov    esi,eax
0x400628    mov    edi,0x4006f4
0x40062d    mov    eax,0x0
0x400632    call   0x4004f0 <printf@plt>
0x400637    mov    edi,0xa
0x40063c    call   0x400659 <test(int)>
0x400641    mov    esi,eax
0x400643    mov    edi,0x4006fd
0x400648    mov    eax,0x0
0x40064d    call   0x4004f0 <printf@plt>

Where the asm block for "test" is:

0x400659    push   rbp 
0x40065a    mov    rbp,rsp
0x40065d    mov    DWORD PTR [rbp-0x4],edi
0x400660    mov    eax,DWORD PTR [rbp-0x4]
0x400663    add    eax,0x19
0x400666    pop    rbp
0x400667    ret

So, as you can see in that situation, it seems to hold pretty much no effect over how GCC produced that code. Even if you do this, you will still get runtime calculations:

int value = test(30);
printf("Value: %u\n", value);

Where that produces this (the address of test changed slightly due to adding some more code):

0x40061c    mov    edi,0x1e
0x400621    call   0x40067a <test(int)>
0x400626    mov    DWORD PTR [rbp-0x4],eax
0x400629    mov    eax,DWORD PTR [rbp-0x4]
0x40062c    mov    esi,eax
0x40062e    mov    edi,0x400714
0x400633    mov    eax,0x0
0x400638    call   0x4004f0 <printf@plt>

It does, however, produce the expected result if you declare the value itself as constexpr:

constexpr int value = test(30);
printf("Value: %u\n", value);

And the associated code is:

0x400623    mov    esi,0x37
0x400628    mov    edi,0x400714
0x40062d    mov    eax,0x0
0x400632    call   0x4004f0 <printf@plt>

So essentially, you're not guaranteed a compile-time calculation if you simply prepend the method declaration with constexpr. You also need to declare your a variable as constexpr and assign to it. Doing such a declaration will actually require the compiler to statically evaluate the result. GCC actually yells at you if you try to do this and "test" isn't declared constexpr:

main.cpp|10|error: call to non-constexpr function ‘int test(int)’|

Ragora
  • 149
  • 9
  • I see, thank you for the detailed explanation. I will study about it. – Carlos Miguel Colanta Jul 13 '15 at 00:51
  • Just a follow up question, if i construct a constexpr constructor, are they automatically constexpr? – Carlos Miguel Colanta Jul 13 '15 at 04:13
  • Under the same compiler, it does not appear to do anything at all if the constructor is declared constexpr. A constructor that looked like this: constexpr Test(int a, int b) : mA(a + 10), mB(b + 20) { } will still have mA and mB calculated at runtime according to the assembly that GCC produced for me. Simply declaring the new itself as constexpr will inherently fail because it's dynamic memory allocation and thusly the compiler cannot know what memory address will be handed back in those situations. – Ragora Jul 13 '15 at 04:19