<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>C&#43;&#43;98 on Uncle Xiang&#39;s Notebook</title>
        <link>https://ttf248.life/en/tags/c-98/</link>
        <description>Recent content in C&#43;&#43;98 on Uncle Xiang&#39;s Notebook</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en</language>
        <lastBuildDate>Fri, 14 Aug 2026 20:08:34 +0800</lastBuildDate><atom:link href="https://ttf248.life/en/tags/c-98/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>C&#43;&#43; Singleton: Initialization Guarantees Behind a Single `static` Line</title>
        <link>https://ttf248.life/en/p/cpp-singleton-static-initialization/</link>
        <pubDate>Fri, 24 Jul 2026 23:32:22 +0800</pubDate>
        
        <guid>https://ttf248.life/en/p/cpp-singleton-static-initialization/</guid>
        <description>&lt;p&gt;Many C++ singleton implementations eventually shrink to this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Instance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// C++11 guarantees thread-safe initialization of function-local static objects.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;instance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;instance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It looks like hiding an object inside a function, and using a reference to retrieve it. What is truly worth discussing is not the singleton pattern itself, but the fact that this code simultaneously touches upon C++ storage duration, scope, references, deferred initialization, and the concurrency semantics that were only completed in C++11.&lt;/p&gt;
&lt;p&gt;First, a correction on the terminology: the &amp;ldquo;C98&amp;rdquo; mentioned in the context should be &amp;ldquo;C++98&amp;rdquo;. Strictly speaking, C has no version called C98; more importantly, C does not have C++&amp;rsquo;s classes, references, or member functions, so the &lt;code&gt;T&amp;amp;&lt;/code&gt; syntax is not C code at all.&lt;/p&gt;
&lt;h2 id=&#34;the-two-static-are-not-the-same-thing&#34;&gt;The Two &lt;code&gt;static&lt;/code&gt; Are Not the Same Thing
&lt;/h2&gt;&lt;p&gt;A typical complete singleton class is usually written like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Singleton&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Singleton&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Instance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Singleton&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;instance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;instance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;Singleton&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Singleton&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;delete&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;Singleton&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;operator&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Singleton&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;delete&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;Singleton&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;~&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Singleton&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The outer &lt;code&gt;static&lt;/code&gt; appears in the class member function declaration, indicating that &lt;code&gt;Instance&lt;/code&gt; is a static member function. It has no &lt;code&gt;this&lt;/code&gt; pointer and can be called directly via &lt;code&gt;Singleton::Instance()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;static&lt;/code&gt; keyword inside the function modifies the local variable &lt;code&gt;instance&lt;/code&gt; within the function body. It is still only visible within this function body, but its storage duration is not the automatic storage duration of &amp;ldquo;creating a new one each time the function is entered&amp;rdquo;; instead, it has static storage duration: the same object is retained throughout the program&amp;rsquo;s execution. Its initialization timing is when the control flow first passes through this declaration, rather than being reconstructed each time the function is called.&lt;/p&gt;
&lt;p&gt;So the &amp;ldquo;singleton&amp;rdquo; here comes from the combination of several conditions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The name of &lt;code&gt;instance&lt;/code&gt; is scoped within the &lt;code&gt;Instance&lt;/code&gt; function, so the outside cannot directly obtain another object with the same name;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;static&lt;/code&gt; makes it retain the same object across multiple calls;&lt;/li&gt;
&lt;li&gt;The constructor is private, so the outside cannot casually use &lt;code&gt;Singleton s&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;The copy constructor and copy assignment are deleted to avoid creating a second object through copying;&lt;/li&gt;
&lt;li&gt;It returns &lt;code&gt;Singleton&amp;amp;&lt;/code&gt;, handing a reference to the same object to the caller, not returning a copy.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The last two items are C++11 syntax. C++98 does not have &lt;code&gt;= delete&lt;/code&gt; and &lt;code&gt;= default&lt;/code&gt;; you can only declare the copy constructor, assignment operator, and constructor in the &lt;code&gt;private&lt;/code&gt; section without defining them.&lt;/p&gt;
&lt;h2 id=&#34;what-does-t-actually-do&#34;&gt;What does &lt;code&gt;T&amp;amp;&lt;/code&gt; actually do
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;T&amp;amp;&lt;/code&gt; can be read from right to left as &amp;ldquo;a reference to something of type &lt;code&gt;T&lt;/code&gt;&amp;rdquo;. Therefore:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Instance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It means &lt;code&gt;Instance&lt;/code&gt; returns a reference to a &lt;code&gt;T&lt;/code&gt; object, not a &lt;code&gt;T&lt;/code&gt; object.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;instance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What is returned here is the static object pointed to by the local name &lt;code&gt;instance&lt;/code&gt;. Because its lifetime spans the entire execution of the program, the reference remains valid after the function returns; if it were a regular local variable, returning a reference this way would result in a dangling reference.&lt;/p&gt;
&lt;p&gt;Returning a reference has two direct effects: the caller receives the original object and can modify it; meanwhile, no additional object copy is made. If read-only access is the only intent, the interface can be written as &lt;code&gt;const T&amp;amp;&lt;/code&gt;, but this will not automatically make other shared state inside &lt;code&gt;T&lt;/code&gt; concurrency-safe.&lt;/p&gt;
&lt;h2 id=&#34;what-the-compiler-actually-needs-to-resolve-on-the-first-call&#34;&gt;What the compiler actually needs to resolve on the first call
&lt;/h2&gt;&lt;p&gt;Breaking down the invocation process into three scenarios makes it clearest.&lt;/p&gt;
&lt;p&gt;On the first call, control flow reaches:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;instance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If the object has not finished initialization, call the default constructor of &lt;code&gt;T&lt;/code&gt;. After construction is complete, execute &lt;code&gt;return instance&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;On subsequent calls, &lt;code&gt;instance&lt;/code&gt; has already been initialized, so the constructor does not execute again, and the code directly returns the same object.&lt;/p&gt;
&lt;p&gt;The problem arises when two threads simultaneously make their first call: they may both find the object &amp;ldquo;not yet initialized&amp;rdquo; at the same time. Without additional synchronization, the object may be constructed twice, one thread may read a half-constructed object, or the initialization flag may become visible to another thread before the object&amp;rsquo;s contents do.&lt;/p&gt;
&lt;p&gt;Implementations typically hide a guard state for the local static. The following is just pseudocode to aid understanding, not the real ABI required by the standard:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;guard_is_complete&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;lock_guard_for_this_static&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;guard_is_complete&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;construct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;mark_guard_complete&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;unlock_guard_for_this_static&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;instance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The key point is not whether there is a variable named &lt;code&gt;guard&lt;/code&gt;, but rather that the entire sequence of &amp;ldquo;checking, constructing, and marking complete&amp;rdquo; must be protected by the implementation using a synchronization mechanism that conforms to the standard. Before initialization is complete, competing threads must wait; only after initialization is complete can they acquire the object.&lt;/p&gt;
&lt;h2 id=&#34;why-c98-cannot-be-considered-thread-safe&#34;&gt;Why C++98 cannot be considered thread-safe
&lt;/h2&gt;&lt;p&gt;There&amp;rsquo;s a common misconception here: C++98 is not incapable of writing this code. The local &lt;code&gt;static&lt;/code&gt; syntax and the &amp;ldquo;initialize only once&amp;rdquo; sequencing semantics already existed in C++98.&lt;/p&gt;
&lt;p&gt;The difference is that C++98 only describes the execution process in a single-threaded abstract machine; it lacks the standardized multi-threading execution, data race, and memory visibility rules introduced in C++11. It makes no guarantee that when two threads simultaneously pass the declaration point for the first time, the initialization must be performed by one thread while the others wait.&lt;/p&gt;
&lt;p&gt;Therefore, the correct statement for this code under C++98 is: lazy-initialized singleton can be implemented in a single-threaded scenario; whether it is safe in a multi-threaded scenario depends on the compiler, runtime library, and platform implementation, and no guarantee can be derived from the C++98 standard itself.&lt;/p&gt;
&lt;p&gt;This kind of C++98 version is especially not made safe by &amp;ldquo;checking the pointer before reallocating&amp;rdquo;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;instance&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;instance&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;instance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Two threads may both pass through &lt;code&gt;instance == 0&lt;/code&gt; at the same time, and create two &lt;code&gt;T&lt;/code&gt; objects separately. Even if you add a double-checked locking pattern — &amp;ldquo;check first, acquire the lock, then check again inside the lock&amp;rdquo; — C++98 lacks a sufficient memory model to guarantee that when the pointer is published, another thread will necessarily see the fully constructed object in the correct order. &lt;code&gt;volatile&lt;/code&gt; is not a thread synchronization tool and cannot fix this problem either.&lt;/p&gt;
&lt;p&gt;If a C++98 project genuinely needs this guarantee, it can only offload the synchronization to platforms or libraries outside the standard, such as POSIX one-time initialization, Windows synchronization primitives, or thread libraries available at the time; alternatively, all accesses can be funneled through a mutex. That&amp;rsquo;s &amp;ldquo;patching in the guarantee with external synchronization,&amp;rdquo; not something this &lt;code&gt;static&lt;/code&gt; line in C++98 provides on its own.&lt;/p&gt;
&lt;h2 id=&#34;c11-changed-the-semantics-not-this-line-of-syntax&#34;&gt;C++11 Changed the Semantics, Not This Line of Syntax
&lt;/h2&gt;&lt;p&gt;The key changes in C++11 are not inventions of:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;T&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;instance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This statement could have been written long ago. C++11 formally incorporated the concurrent initialization rules into the language standard: function-local variables with static storage duration are dynamically initialized when the control flow first passes through the declaration; if multiple threads enter concurrently while the variable is being initialized, other executions should wait for the initialization to complete.&lt;/p&gt;
&lt;p&gt;This is precisely what &amp;ldquo;thread-safe initialization&amp;rdquo; means in the comment. What it guarantees is the initialization phase: &lt;code&gt;T&lt;/code&gt;&amp;rsquo;s constructor only successfully executes once, and competing threads will not bypass the unfinished construction to directly use the object.&lt;/p&gt;
&lt;p&gt;It does not guarantee that the business code below is inherently secure:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Singleton&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Instance&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// Multiple threads modifying the same object can still cause data races
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If &lt;code&gt;append&lt;/code&gt; modifies a shared container, you still need to use mutexes, atomic variables, or other proper concurrency design inside &lt;code&gt;T&lt;/code&gt;. A singleton only addresses &amp;ldquo;when and how the object is constructed exactly once&amp;rdquo;; it does not address &amp;ldquo;how each operation inside the object is executed concurrently.&amp;rdquo;&lt;/p&gt;
&lt;h2 id=&#34;a-few-corner-cases-in-the-c11-approach&#34;&gt;A few corner cases in the C++11 approach
&lt;/h2&gt;&lt;p&gt;First, &lt;code&gt;static T instance;&lt;/code&gt; requires &lt;code&gt;T&lt;/code&gt; to be default-constructible, with the constructor and destructor accessible here. If the constructor throws, the initialization is considered incomplete; the next time control flow reaches the declaration, initialization will be retried, rather than leaving a half-constructed object behind.&lt;/p&gt;
&lt;p&gt;Second, the initialization function must not recursively re-enter the initialization process of the same function-local static. For example, if the constructor of &lt;code&gt;T&lt;/code&gt; calls &lt;code&gt;Instance()&lt;/code&gt;, this enters a recursive initialization scenario that the standard explicitly prohibits, and cannot be treated as ordinary reentrance.&lt;/p&gt;
&lt;p&gt;Third, if the &lt;code&gt;instance&lt;/code&gt; is actually constructed, it will be destructed during the program&amp;rsquo;s termination phase. This characteristic is usually healthier than deliberately leaking memory after manually writing &lt;code&gt;new&lt;/code&gt;, but it also means it may depend on the destruction order of other global objects. It&amp;rsquo;s best not to covertly rely on a bunch of cross-file global state inside the singleton&amp;rsquo;s constructor.&lt;/p&gt;
&lt;p&gt;Finally, the &amp;ldquo;globally unique&amp;rdquo; nature of a singleton is mainly a single object at the language level. When crossing dynamic library, plugin, or different runtime boundaries, whether it is truly unique to the entire process also depends on the linking and loading boundaries; you cannot assume that all modules share the same instance based solely on the class name.&lt;/p&gt;
&lt;h2 id=&#34;conclusion-remember-this-distinction&#34;&gt;Conclusion: Remember This Distinction
&lt;/h2&gt;&lt;p&gt;Both C++98 and C++11 can write local static singletons; only C++11 codified the guarantee in the standard that, during concurrent initialization, the initialization happens only once while other threads wait.&lt;/p&gt;
&lt;p&gt;So when you see code like this, it&amp;rsquo;s best to check it in three layers: &lt;code&gt;static&lt;/code&gt; keeps the object alive for a long time, the function scope narrows the entry point, and the C++11 language rules are what give the first concurrent initialization a portable guarantee. As for whether the data inside the object can be modified simultaneously by multiple threads after construction completes—that&amp;rsquo;s a different question.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://timsong-cpp.github.io/cppwp/n3337/stmt.dcl&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;C++11 Working Draft N3337: 6.7 Declaration statement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://eel.is/c&amp;#43;&amp;#43;draft/stmt.dcl&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Current C++ Working Draft: [stmt.dcl]&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1875.html&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;WG21 N1875: C++ Threads&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://learn.microsoft.com/en-us/cpp/build/reference/zc-threadsafeinit-thread-safe-local-static-initialization?view=msvc-170&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Microsoft: /Zc:threadSafeInit (Thread-safe local static initialization)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;details class=&#34;article-notes&#34;&gt;
    &lt;summary&gt;写作附记&lt;/summary&gt;
    &lt;div class=&#34;article-notes__content&#34;&gt;
        &lt;h3 id=&#34;original-prompt&#34;&gt;Original Prompt
&lt;/h3&gt;&lt;blockquote&gt;
&lt;p&gt;$blog-writer C++ Singleton, please explain in detail the principle behind this. How is it supported at the language syntax level? &lt;code&gt;static T &amp;amp;Instance() { // C++11 guarantees thread-safe initialization of function-local static objects. static T instance; return instance; }&lt;/code&gt; Why couldn&amp;rsquo;t this be done in C++98? Why can it be done in C++11?&lt;/p&gt;
&lt;/blockquote&gt;
    &lt;/div&gt;
&lt;/details&gt;</description>
        </item>
        
    </channel>
</rss>
