Search code examples
c++staticstatic-initialization

Is a static initializer executed for global const POD type?


Following https://meowni.ca/posts/static-initializers/, I am trying to reduce static initializers in our shared libraries to improve startup time of our program. One of the suggestions in the article is to avoid objects with non-trivial constructors as static objects. I'm trying to measure if the total static initializers came down after implementing the same.

Consider the following program:

#include <iostream>

static const char foo[] = "1234567890";
static const std::string bar = "abcdefghij";

int main() {
    std::cout << foo << std::endl;
    std::cout << bar << std::endl;
}

When I compile and inspect the ELF, I see function pointers for both foo and bar in the .init_array section which supposedly holds static initializers

readelf -s .init_array ./a.out -W | egrep "foo|bar"
    11: 0000000000403010    11 OBJECT  LOCAL  DEFAULT   15 _ZL3foo
    12: 0000000000405200    32 OBJECT  LOCAL  DEFAULT   25 _ZL3bar

Since foo is compile-time constant, it shouldn't require a static initializer right?


Solution

  • Whether the type is const, trivial or POD is irrelevant.

    Whether or not dynamic initialization at runtime may happen is determined by whether or not the initialization is a constant expression.

    foo's initialization in your example is guaranteed to be a constant expression.

    bar's initialization in your example is guaranteed not to be a constant expression before C++20 and may or may not be one in C++20 (depending on how exactly std::string is implemented, in particular whether SSO is being used for the initialization).

    The output of readelf is not giving you the dynamic initializers, but just the symbols for the global objects themselves. You can see this in the mangled name of the symbol or that it says OBJECT instead of FUNC. It has no relevance to your problem.

    Since C++20 you can enforce that no dynamic initialization will happen by declaring the variable with constinit. Then either you are guaranteed that there is no dynamic initialization (because the initializer is a constant expression) or the compiler will fail to compile the program, letting you know why it isn't a constant expression.

    Also static probably doesn't do what you think it does in your example. static here determines linkage, not storage duration, so it just means that foo and bar shall have internal instead of external linkage, meaning they will not be the same variable if declared like this in multiple translation units. They have static storage duration regardless of whether or not you use static, because they are declared at namespace scope.