Magic statics vs. std::call_once

The Old New Thing (Raymond Chen) News

Summary

The article compares two C++ techniques, magic statics and std::call_once, for caching computations, explaining when to use each based on factors like scope and initialization.

<p>Suppose you have some function like</p> <pre>bool should_use_widgets() { bool supported = ⟦ complex code to check OS features ⟧; return supported &amp;&amp; is_configuration_enabled("widgets"); } </pre> <p>Since OS Widget support is not something that changes during the lifetime of the program, you want to calculate it once and cache the result.</p> <p>One way is to use a so-called &#8220;magic static&#8221;:</p> <pre>bool should_use_widgets() { static const bool supported = [] { return ⟦ complex code to check OS features ⟧; }(); return supported &amp;&amp; is_configuration_enabled("widgets"); } </pre> <p>Function-local statics are initialized the first time execution reaches the variable. On subsequent executions, nothing happens.</p> <p>Another way is to use <code>std::call_once</code>.</p> <pre>bool is_supported_cached; std::once_flag is_supported_once; bool are_widgets_supported() { std::call_once(is_supported_once, [] { is_supported_cached = ⟦ complex code to check OS features ⟧; }); return is_supported_cached &amp;&amp; is_configuration_enabled("widgets"); } </pre> <p>Why would you choose one over the other?</p> <p>Magic statics are certainly more convenient. You don&#8217;t have to juggle two variables. You just declare a function-local <code>static</code> and initialize it. One problem is that they have to be a function-local static. Multiple functions can&#8217;t access that same cached variable. But that&#8217;s easy to work around: Have a function whose sole job is to manage that one static.</p> <pre>bool are_widgets_supported_in_os() { static const bool supported = [] { return ⟦ complex code to check OS features ⟧; }(); return supported; } bool are_widgets_supported() { return are_widgets_supported_in_os() &amp;&amp; is_configuration_enabled("widgets"); } bool are_widget_carriers_supported() { return are_widgets_supported_in_os() &amp;&amp; is_configuration_enabled("widget_carriers"); } </pre> <p>This trick is often used for singleton patterns.</p> <pre>class Singleton { public: static Singleton&amp; GetInstance() { static Singleton instance; return instance; } ⟦ various methods go here ⟧; private: Singleton() = default; Singleton(Singleton const&amp;) = delete; Singleton&amp; operator=(Singleton const&amp;) = delete; ~Singleton() = default; } </pre> <p>So when would you use <code>call_once</code>?</p> <p>Magic statics work only for statics. Maybe you want to lazy-initialize a non-static data member.</p> <p>Suppose we have a <code>Gadget</code> that is constructed with an associated <code>Widget</code>. And suppose that the <code>Gadget</code> support for polarity reversal is dependent on whether the <code>Widget</code> supports polarity reversal. Furthermore, polarity reversibility is expensive to calculate, but since it is an immutable property, we can calculate it only once and cache the result.</p> <pre>class Gadget { public: Gadget(std::shared_ptr&lt;Widget&gt; const&amp; widget) : widget(widget) {} bool can_reverse_polarity() { return can_reverse_polarity_cached; } private: std::shared_ptr&lt;Widget&gt; const widget; bool can_reverse_polarity_cached = is_configuration_enabled("polarity_reversal") &amp;&amp; is_widget_polarity_reversible(*widget); }; </pre> <p>The <code>can_<wbr />reverse_<wbr />polarity_<wbr />cached</code> is a non-static data member with an explicit initializer, so it initializes at the construction of the <code>Gadget</code> class, rather than initializing on demand the first time somebody calls <code>can_<wbr />reverse_<wbr />polarity</code>.</p> <p>&#8220;No problem,&#8221; you say. &#8220;I can use a magic static.&#8221;</p> <pre> bool can_reverse_polarity() { <span style="border: solid 1px currentcolor; border-bottom: none;">static bool can_reverse_polarity_cached = </span> <span style="border: 1px currentcolor; border-style: none solid;"> is_configuration_enabled("polarity_reversal") &amp;&amp;</span> <span style="border: solid 1px currentcolor; border-top: none;"> is_widget_polarity_reversible(*widget); </span> return can_reverse_polarity_cached; } </pre> <p>Function-static variables in a member function are static with respect to the member function. All instances of <code>Gadget</code> share the same member function, and therefore they all share the same <code>can_<wbr />reverse_<wbr />polarity_<wbr />cached</code> variable. The time you call <code>Gadget::<wbr />can_<wbr />reverse_<wbr />polarity()</code>, it calculates the reversibility of the <code>Widget</code> that is associated with the <code>Gadget</code> you called it from, and that value is then locked in for all future calls to <code>Gadget::<wbr />can_<wbr />reverse_<wbr />polarity()</code>, even though the future calls may be on unrelated <code>Gadget</code>s.</p> <p>What we want is a variant of magic statics that initialize for each <i>instance</i> of the class, rather than once for all instances.</p> <p>That&#8217;s the case for <code>std::<wbr />call_once</code>.</p> <pre>class Gadget { public: Gadget(std::shared_ptr&lt;Widget&gt; const&amp; widget) : widget(widget) {} bool can_reverse_polarity() { <span style="border: solid 1px currentcolor; border-bottom: none;">std::call_once(can_reverse_polarity_once, [] { </span> <span style="border: 1px currentcolor; border-style: none solid;"> can_reverse_polarity_cached = </span> <span style="border: 1px currentcolor; border-style: none solid;"> is_configuration_enabled("polarity_reversal") &amp;&amp;</span> <span style="border: 1px currentcolor; border-style: none solid;"> is_widget_polarity_reversible(*widget); </span> <span style="border: solid 1px currentcolor; border-top: none;">}); </span> return can_reverse_polarity_cached; } private: std::shared_ptr&lt;Widget&gt; const widget; <span style="border: solid 1px currentcolor; border-bottom: none;">bool can_reverse_polarity_cached; // initializes on demand</span> <span style="border: solid 1px currentcolor; border-top: none;">std::once_flag can_reverse_polarity_once; </span> }; </pre> <p>I guess you could encapsulate this in a <code>lazy&lt;T&gt;</code> type.¹</p> <pre>template&lt;typename T, typename L&gt; struct lazy { lazy(L&amp;&amp; l) : init(std::forward&lt;L&gt;(l)) {} T&amp; get() { std::call_once(once, [&amp;] { value.emplace(init()); }); return *value; } private: std::optional&lt;T&gt; value; std::once_flag once; std::decay_t&lt;L&gt; init; }; template&lt;typename T, typename L&gt; lazy&lt;T, L&gt; make_lazy(L&amp;&amp; l) { return { std::forward&lt;L&gt;(l) }; } void test() { auto v = make_lazy&lt;int&gt;([] { printf("Slow calculation\n"); return 42; }); printf("Value is %d\n", v.get()); printf("Value is still %d\n", v.get()); } </pre> <p>But wait, we also have <code>std::async</code> with deferred execution. Should we use that? We&#8217;ll look at this question next time.</p> <p>¹ Note that this is not the same as <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2506r0.pdf"> the <code>std::lazy</code> proposal</a>.</p> <p>The post <a href="https://devblogs.microsoft.com/oldnewthing/20260916-00/?p=112703">Magic statics vs. &lt;CODE&gt;std::call_once&lt;/CODE&gt;</a> appeared first on <a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>.</p>
Original Article
View Cached Full Text

Cached at: 09/17/26, 02:31 PM

# Magic statics vs. std::call_once - The Old New Thing Source: [https://devblogs.microsoft.com/oldnewthing/20260916-00?p=112703](https://devblogs.microsoft.com/oldnewthing/20260916-00?p=112703) Suppose you have some function like ``` bool should_use_widgets() { bool supported = ⟦ complex code to check OS features ⟧; return supported && is_configuration_enabled("widgets"); } ``` Since OS Widget support is not something that changes during the lifetime of the program, you want to calculate it once and cache the result\. One way is to use a so\-called “magic static”: ``` bool should_use_widgets() { static const bool supported = [] { return ⟦ complex code to check OS features ⟧; }(); return supported && is_configuration_enabled("widgets"); } ``` Function\-local statics are initialized the first time execution reaches the variable\. On subsequent executions, nothing happens\. Another way is to use`std::call\_once`\. ``` bool is_supported_cached; std::once_flag is_supported_once; bool are_widgets_supported() { std::call_once(is_supported_once, [] { is_supported_cached = ⟦ complex code to check OS features ⟧; }); return is_supported_cached && is_configuration_enabled("widgets"); } ``` Why would you choose one over the other? Magic statics are certainly more convenient\. You don’t have to juggle two variables\. You just declare a function\-local`static`and initialize it\. One problem is that they have to be a function\-local static\. Multiple functions can’t access that same cached variable\. But that’s easy to work around: Have a function whose sole job is to manage that one static\. ``` bool are_widgets_supported_in_os() { static const bool supported = [] { return ⟦ complex code to check OS features ⟧; }(); return supported; } bool are_widgets_supported() { return are_widgets_supported_in_os() && is_configuration_enabled("widgets"); } bool are_widget_carriers_supported() { return are_widgets_supported_in_os() && is_configuration_enabled("widget_carriers"); } ``` This trick is often used for singleton patterns\. ``` class Singleton { public: static Singleton& GetInstance() { static Singleton instance; return instance; } ⟦ various methods go here ⟧; private: Singleton() = default; Singleton(Singleton const&) = delete; Singleton& operator=(Singleton const&) = delete; ~Singleton() = default; } ``` So when would you use`call\_once`? Magic statics work only for statics\. Maybe you want to lazy\-initialize a non\-static data member\. Suppose we have a`Gadget`that is constructed with an associated`Widget`\. And suppose that the`Gadget`support for polarity reversal is dependent on whether the`Widget`supports polarity reversal\. Furthermore, polarity reversibility is expensive to calculate, but since it is an immutable property, we can calculate it only once and cache the result\. ``` class Gadget { public: Gadget(std::shared_ptr<Widget> const& widget) : widget(widget) {} bool can_reverse_polarity() { return can_reverse_polarity_cached; } private: std::shared_ptr<Widget> const widget; bool can_reverse_polarity_cached = is_configuration_enabled("polarity_reversal") && is_widget_polarity_reversible(*widget); }; ``` The`can\_reverse\_polarity\_cached`is a non\-static data member with an explicit initializer, so it initializes at the construction of the`Gadget`class, rather than initializing on demand the first time somebody calls`can\_reverse\_polarity`\. “No problem,” you say\. “I can use a magic static\.” ``` bool can_reverse_polarity() { static bool can_reverse_polarity_cached = is_configuration_enabled("polarity_reversal") && is_widget_polarity_reversible(*widget); return can_reverse_polarity_cached; } ``` Function\-static variables in a member function are static with respect to the member function\. All instances of`Gadget`share the same member function, and therefore they all share the same`can\_reverse\_polarity\_cached`variable\. The time you call`Gadget::can\_reverse\_polarity\(\)`, it calculates the reversibility of the`Widget`that is associated with the`Gadget`you called it from, and that value is then locked in for all future calls to`Gadget::can\_reverse\_polarity\(\)`, even though the future calls may be on unrelated`Gadget`s\. What we want is a variant of magic statics that initialize for each*instance*of the class, rather than once for all instances\. That’s the case for`std::call\_once`\. ``` class Gadget { public: Gadget(std::shared_ptr<Widget> const& widget) : widget(widget) {} bool can_reverse_polarity() { std::call_once(can_reverse_polarity_once, [] { can_reverse_polarity_cached = is_configuration_enabled("polarity_reversal") && is_widget_polarity_reversible(*widget); }); return can_reverse_polarity_cached; } private: std::shared_ptr<Widget> const widget; bool can_reverse_polarity_cached; // initializes on demand std::once_flag can_reverse_polarity_once; }; ``` I guess you could encapsulate this in a`lazy<T\>`type\.¹ ``` template<typename T, typename L> struct lazy { lazy(L&& l) : init(std::forward<L>(l)) {} T& get() { std::call_once(once, [&] { value.emplace(init()); }); return *value; } private: std::optional<T> value; std::once_flag once; std::decay_t<L> init; }; template<typename T, typename L> lazy<T, L> make_lazy(L&& l) { return { std::forward<L>(l) }; } void test() { auto v = make_lazy<int>([] { printf("Slow calculation\n"); return 42; }); printf("Value is %d\n", v.get()); printf("Value is still %d\n", v.get()); } ``` But wait, we also have`std::async`with deferred execution\. Should we use that? We’ll look at this question next time\. ¹ Note that this is not the same as[the`std::lazy`proposal](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2506r0.pdf)\. ### Category ### Topics ## Author ![Raymond Chen](https://devblogs.microsoft.com/oldnewthing/wp-content/uploads/sites/38/2019/02/RaymondChen_5in-150x150.jpg) Raymond has been involved in the evolution of Windows for more than 30 years\. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie\-jeebies\. The Web site spawned a book, coincidentally also titled The Old New Thing \(Addison Wesley 2007\)\. He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information\.

Similar Articles

std::call_once vs. std::async

The Old New Thing (Raymond Chen)

The article compares std::call_once and std::async for deferred execution in C++, discussing their differences in implementation, performance, and exception behavior.

Static Allocation, Constant Work

Lobsters Hottest

This article explores static allocation strategies to prevent memory safety bugs such as use-after-free and type confusion, discussing object pools and generational indices, and introducing tricks from TigerStyle for avoiding dynamic memory allocation after initialization.

Being lazy in C++

Lobsters Hottest

This article explains lazy initialization in C++ to optimize performance by deferring computations until necessary, using a helper struct to exploit conversion operators in std::optional.