Difference between `constexpr` and `const`

后端 未结 9 1473
无人共我
无人共我 2020-11-22 03:48

What\'s the difference between constexpr and const?

  • When can I use only one of them?
  • When can I use both and how should I
9条回答
  •  别跟我提以往
    2020-11-22 04:17

    I don't think any of the answers really make it clear exactly what side effects it has, or indeed, what it is.

    constexpr and const at namespace/file-scope are identical when initialised with a literal or expression; but with a function, const can be initialised by any function, but constexpr initialised by a non-constexpr (a function that isn't marked with constexpr or a non constexpr expression) will generate a compiler error. Both constexpr and const are implicitly internal linkage for variables (well actually, they don't survive to get to the linking stage if compiling -O1 and stronger, and static doesn't force the compiler to emit an internal (local) linker symbol for const or constexpr when at -O1 or stronger; the only time it does this is if you take the address of the variable. const and constexpr will be an internal symbol unless expressed with extern i.e. extern constexpr/const int i = 3; needs to be used). On a function, constexpr makes the function permanently never reach the linking stage (regardless of extern or inline in the definition or -O0 or -Ofast), whereas const never does, and static and inline only have this effect on -O1 and above. When a const/constexpr variable is initialised by a constexpr function, the load is always optimised out with any optimisation flag, but it is never optimised out if the function is only static or inline, or if the variable is not a const/constexpr.

    Standard compilation (-O0)

    #include
    constexpr int multiply (int x, int y)
    {
    
      return x * y;
    }
    
    extern const int val = multiply(10,10);
    int main () {
      std::cout << val;
    } 
    

    compiles to

    val:
            .long   100  //extra external definition supplied due to extern
    
    main:
            push    rbp
            mov     rbp, rsp
            mov     esi, 100 //substituted in as an immediate
            mov     edi, OFFSET FLAT:_ZSt4cout
            call    std::basic_ostream >::operator<<(int)
            mov     eax, 0
            pop     rbp
            ret
    
    __static_initialization_and_destruction_0(int, int):
            . 
            . 
            . 
    

    However

    #include
    const int multiply (int x, int y)
    {
    
      return x * y;
    }
    
    const int val = multiply(10,10); //constexpr is an error
    int main () {
      std::cout << val;
    }
    

    Compiles to

    multiply(int, int):
            push    rbp
            mov     rbp, rsp
            mov     DWORD PTR [rbp-4], edi
            mov     DWORD PTR [rbp-8], esi
            mov     eax, DWORD PTR [rbp-4]
            imul    eax, DWORD PTR [rbp-8]
            pop     rbp
            ret
    
    main:
            push    rbp
            mov     rbp, rsp
            mov     eax, DWORD PTR val[rip]
            mov     esi, eax
            mov     edi, OFFSET FLAT:_ZSt4cout
            call    std::basic_ostream >::operator<<(int)
            mov     eax, 0
            pop     rbp
            ret
    
    __static_initialization_and_destruction_0(int, int):
            . 
            . 
            . 
            mov     esi, 10
            mov     edi, 10
            call    multiply(int, int)
            mov     DWORD PTR val[rip], eax
    

    This clearly shows that constexpr causes the initialisation of the const/constexpr file-scope variable to occur at compile time and produce no global symbol, whereas not using it causes initialisation to occur before main at runtime.

    Compiling using -Ofast

    Even -Ofast doesn't optimise out the load! https://godbolt.org/z/r-mhif, so you need constexpr


    constexpr functions can also be called from inside other constexpr functions for the same result. constexpr on a function also prevents use of anything that can't be done at compile time in the function; for instance, a call to the << operator on std::cout.

    constexpr at block scope behaves the same in that it produces an error if initialised by a non-constexpr function; the value is also substituted in immediately.

    In the end, its main purpose is like C's inline function, but it is only effective when the function is used to initialise file-scope variables (which functions cannot do on C, but they can on C++ because it allows dynamic initialisation of file-scope variables), except the function cannot export a global/local symbol to the linker as well, even using extern/static, which you could with inline on C; block-scope variable assignment functions can be inlined simply using an -O1 optimisation without constexpr on C and C++.

提交回复
热议问题