Other answers already mentioned that call_once
effect may be achieved with magic static, or with mutex
usage.
I want to focus on performance and corner case handling.
Using mutex
will be slower than call_once
on fast path (when the target function is already called). That's because mutex
acquisition/release will be atomic RMWs, whereas call_once
queries are just reads. You can fix mutex
performance by implementing double-check lock, but this will complicate the program.
Using call_once
is also safer than mutex, if the target function is first attempted to be called only on program exit. mutex
isn't necessarily trivially destructible, so a global mutex may be destroyed by the time the target is to be called.
Magic static is also likely to be fast on fast path by making only reads to query if the object is already initialized. The performance of magic static vs call_once
may depend on implementation, but generally magic static has more opportunity for the optimization, as it doesn't have to expose the state in a variable and has to be implemented by the compiler rather than only in the library.
MSVC specific: magic statics employ thread-local storage. This is more optimal than call_once
. But magic static support can be turned off by /Zc:threadSafeInit-. The ability to turn it off is due to some thread-local storage disadvantages. call_once
cannot be turned off. Before Visual Studio 2015, call_once
wasn't implemented well, and magic statics weren't implemented at all, so it was hard to get one-time initialization without 3rd party libs.