Search code examples
c++staticconstexpr

When is it better to use "static constexpr" than "constexpr"?


In the answers to this question, (static constexpr vs constexpr in function body?), it is stated that static constexpr puts the variable in the .rodata section, whereas constexpr alone may allow initialising the variable at run-time.

IMPORTANT: This questions is about static variables within a function, not to do with linking external libraries.

My question is: in what situation would we prefer one of these two to the other in the code, in practice? Can one be better than the other for some codes? It is very hard to imagine how this can happen. After all, with either options, we can write codes in exactly the same way, with the same computational result. And the compiler can presumably optimise to make the better choice. So what does the programmer need to judge when choosing?


Solution

  • If you don't use a constexpr variable as an lvalue (i.e., you only read its value but don't use its address) then its storage duration doesn't matter. The static constexpr variable and automatic constexpr variable are not odr-used (only their value is used), and will be optimized out. For instance, with GCC:

    int f(bool b) {
        int unused = 999;  // At -O1, this is optimized out
        constexpr int x = 123;  // At -O1, this is optimized out
        static constexpr int y = 456;  // At -O0, this is optimized out
        if (b) {
            return x;  // Replaced with "return 123;" at -O0 (x not actually used)
        } else {
            return y;  // Replaced with "return 456;" at -O0
        }
    }
    

    However, there is a difference when used as an lvalue. It can make your code wrong:

    const char* name(bool b) {
        static constexpr char name[2][6] = { "false", "true\0" };
        return name[b];  // Would be dangling if name was not `static`
    }
    

    Or it can have performance impacts:

    unsigned permute(unsigned i) {
        [[assume(i < 10)]];
        constexpr int table[10] = { 4, 5, 8, 2, 3, 1, 9, 6, 0, 7 };
        return table[i];
    }
    

    Since table was not declared static, it will copy the initializer onto the stack and then index the stack. The initializer will be stored in the .rodata section. If table was static, return table[i]; would just index into something in the .rodata section.

    It looks like clang is smart enough to optimize this specific case (GCC isn't), but there will be differences if you want to pass a large constant block to a void unknown_external_function(const void*); (the compiler will be forced to copy onto the stack if the constexpr variable isn't static).

    It's hard to think of a situation where you would want to use an automatic constexpr variable over a static constexpr variable, other than pre-C++23 where constexpr functions can't declare static constexpr variables.