{"id":112703,"date":"2026-09-16T07:00:00","date_gmt":"2026-09-16T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112703"},"modified":"2026-09-17T07:21:41","modified_gmt":"2026-09-17T14:21:41","slug":"20260916-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260916-00\/?p=112703","title":{"rendered":"Magic statics vs. <CODE>std::call_once<\/CODE>"},"content":{"rendered":"<p>Suppose you have some function like<\/p>\n<pre>bool should_use_widgets()\r\n{\r\n    bool supported = \u27e6 complex code to check OS features \u27e7;\r\n    return supported &amp;&amp; is_configuration_enabled(\"widgets\");\r\n}\r\n<\/pre>\n<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>\n<p>One way is to use a so-called &#8220;magic static&#8221;:<\/p>\n<pre>bool should_use_widgets()\r\n{\r\n    static const bool supported = [] {\r\n        return \u27e6 complex code to check OS features \u27e7;\r\n    }();\r\n    return supported &amp;&amp; is_configuration_enabled(\"widgets\");\r\n}\r\n<\/pre>\n<p>Function-local statics are initialized the first time execution reaches the variable. On subsequent executions, nothing happens.<\/p>\n<p>Another way is to use <code>std::call_once<\/code>.<\/p>\n<pre>bool is_supported_cached;\r\nstd::once_flag is_supported_once;\r\n\r\nbool are_widgets_supported()\r\n{\r\n    std::call_once(is_supported_once, [] {\r\n        is_supported_cached = \u27e6 complex code to check OS features \u27e7;\r\n    });\r\n    return is_supported_cached &amp;&amp; is_configuration_enabled(\"widgets\");\r\n}\r\n<\/pre>\n<p>Why would you choose one over the other?<\/p>\n<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>\n<pre>bool are_widgets_supported_in_os()\r\n{\r\n    static const bool supported = [] {\r\n        return \u27e6 complex code to check OS features \u27e7;\r\n    }();\r\n    return supported;\r\n}\r\n\r\nbool are_widgets_supported()\r\n{\r\n    return are_widgets_supported_in_os() &amp;&amp;\r\n        is_configuration_enabled(\"widgets\");\r\n}\r\n\r\nbool are_widget_carriers_supported()\r\n{\r\n    return are_widgets_supported_in_os() &amp;&amp;\r\n        is_configuration_enabled(\"widget_carriers\");\r\n}\r\n<\/pre>\n<p>This trick is often used for singleton patterns.<\/p>\n<pre>class Singleton\r\n{\r\npublic:\r\n    static Singleton&amp; GetInstance()\r\n    {\r\n        static Singleton instance;\r\n        return instance;\r\n    }\r\n\r\n    \u27e6 various methods go here \u27e7;\r\n\r\nprivate:\r\n    Singleton() = default;\r\n    Singleton(Singleton const&amp;) = delete;\r\n    Singleton&amp; operator=(Singleton const&amp;) = delete;\r\n    ~Singleton() = default;\r\n}\r\n<\/pre>\n<p>So when would you use <code>call_once<\/code>?<\/p>\n<p>Magic statics work only for statics. Maybe you want to lazy-initialize a non-static data member.<\/p>\n<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>\n<pre>class Gadget\r\n{\r\npublic:\r\n    Gadget(std::shared_ptr&lt;Widget&gt; const&amp; widget) : widget(widget) {}\r\n\r\n    bool can_reverse_polarity()\r\n    {\r\n        return can_reverse_polarity_cached;\r\n    }\r\n\r\nprivate:\r\n    std::shared_ptr&lt;Widget&gt; const widget;\r\n    bool can_reverse_polarity_cached =\r\n        is_configuration_enabled(\"polarity_reversal\") &amp;&amp;\r\n        is_widget_polarity_reversible(*widget);\r\n};\r\n<\/pre>\n<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>\n<p>&#8220;No problem,&#8221; you say. &#8220;I can use a magic static.&#8221;<\/p>\n<pre>    bool can_reverse_polarity()\r\n    {\r\n        <span style=\"border: solid 1px currentcolor; border-bottom: none;\">static bool can_reverse_polarity_cached =           <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">    is_configuration_enabled(\"polarity_reversal\") &amp;&amp;<\/span>\r\n        <span style=\"border: solid 1px currentcolor; border-top: none;\">    is_widget_polarity_reversible(*widget);         <\/span>\r\n\r\n        return can_reverse_polarity_cached;\r\n    }\r\n<\/pre>\n<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>\n<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>\n<p>That&#8217;s the case for <code>std::<wbr \/>call_once<\/code>.<\/p>\n<pre>class Gadget\r\n{\r\npublic:\r\n    Gadget(std::shared_ptr&lt;Widget&gt; const&amp; widget) : widget(widget) {}\r\n\r\n    bool can_reverse_polarity()\r\n    {\r\n        <span style=\"border: solid 1px currentcolor; border-bottom: none;\">std::call_once(can_reverse_polarity_once, [] {          <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">    can_reverse_polarity_cached =                       <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">        is_configuration_enabled(\"polarity_reversal\") &amp;&amp;<\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">        is_widget_polarity_reversible(*widget);         <\/span>\r\n        <span style=\"border: solid 1px currentcolor; border-top: none;\">});                                                     <\/span>\r\n        return can_reverse_polarity_cached;\r\n    }\r\n\r\nprivate:\r\n    std::shared_ptr&lt;Widget&gt; const widget;\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">bool can_reverse_polarity_cached; \/\/ initializes on demand<\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">std::once_flag can_reverse_polarity_once;                 <\/span>\r\n};\r\n<\/pre>\n<p>I guess you could encapsulate this in a <code>lazy&lt;T&gt;<\/code> type.\u00b9<\/p>\n<pre>template&lt;typename T, typename L&gt;\r\nstruct lazy\r\n{\r\n    lazy(L&amp;&amp; l) : init(std::forward&lt;L&gt;(l)) {}\r\n\r\n    T&amp; get() {\r\n        std::call_once(once, [&amp;] {\r\n            value.emplace(init());\r\n        });\r\n        return *value;\r\n    }\r\nprivate:\r\n    std::optional&lt;T&gt; value;\r\n    std::once_flag once;\r\n    std::decay_t&lt;L&gt; init;\r\n};\r\n\r\ntemplate&lt;typename T, typename L&gt;\r\nlazy&lt;T, L&gt; make_lazy(L&amp;&amp; l)\r\n{\r\n    return { std::forward&lt;L&gt;(l) };\r\n}\r\n\r\nvoid test()\r\n{\r\n    auto v = make_lazy&lt;int&gt;([] {\r\n        printf(\"Slow calculation\\n\");\r\n        return 42;\r\n    });\r\n\r\n    printf(\"Value is %d\\n\", v.get());\r\n    printf(\"Value is still %d\\n\", v.get());\r\n}\r\n<\/pre>\n<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>\n<p>\u00b9 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>\n","protected":false},"excerpt":{"rendered":"<p>They sort of do the same thing, but differently.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-112703","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>They sort of do the same thing, but differently.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112703","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=112703"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112703\/revisions"}],"predecessor-version":[{"id":112704,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112703\/revisions\/112704"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=112703"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112703"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112703"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}