Singletons get a bad rep, but I argue that it shouldn’t always be the case.

`` As a C++ developer, performance usually matters. When I see a function which does some work and expected to return the same value throughout the program lifetime - I start wondering, can it somehow be optimized?

C++11 introduced a core language feature which enables static storage duration for local variables. That basically means that a variable declared static inside a function will be initialized exactly once, in a thread safe manner. More specifically, C++11 introduced static storage duration for local variables which also have thread-safe initialization. Prior to C++11, you couldn’t assume thread safety when accessing the singleton from multiple threads, whereas now you are guaranteed that the value is initialized exactly once, without any race conditions/deadlocks between threads.

All of this means that the value will be initialized once on the first call to the function, and will be de-allocated from memory automatically during the program exit.

This design pattern is called “Meyers Singleton”, and here is it in practice:

/* includes */
using system_clock = std::chrono::system_clock;
using namespace std::chrono_literals;

const std::string& get_static_message() {
    static const std::string msg = std::to_string(system_clock::now().time_since_epoch().count());
    return msg;
}

int main() {
    std::cout << std::format("First invocation: {}\n", get_static_message());
    std::this_thread::sleep_for(5s);
    std::cout << std::format("Second invocation: {}\n", get_static_message());
}

In this example the msg variable will be constructed exactly once, on the first call to get_static_message. Here is a possible output of this program, note that the result from the get_static_message is the same!

First invocation:   16614610160060892
Second invocation:  16614610160060892

Another interesting thing to note here is the return value of the singleton is const std::string&, i.e a const ref object. Because the lifetime of the msg object is known be the entire lifetime of the program, we know for certain that it’s memory address is safe to access, and therefore can return a reference instead of the copy we would normally have to create and return.

The full snippet can be found here: Github Gist.