{"id":111228,"date":"2025-05-29T07:00:00","date_gmt":"2025-05-29T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=111228"},"modified":"2025-06-02T18:18:52","modified_gmt":"2025-06-03T01:18:52","slug":"20250529-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250529-00\/?p=111228","title":{"rendered":"The case of creating new instances when you wanted to use the same one"},"content":{"rendered":"<p>A colleague of mine was trying to debug some code that they wrote. They wanted to create an object on demand, but their function to return the on-demand object kept creating new instances rather than reusing the previously-created one.<\/p>\n<p>Here&#8217;s a simplified version.<\/p>\n<pre>\/\/ For expository simplicity, assume single-threaded objects\r\n\r\nstruct Widget\r\n{\r\n    Widget(bool debugMode = false);\r\n\r\n    int m_count = 0;\r\n    void Increment() { ++m_count; }\r\n};\r\n\r\nstruct Doodad\r\n{\r\n    std::unique_ptr&lt;Widget&gt; m_widget; \/\/ created on demand\r\n\r\n    Widget GetWidget()\r\n    {\r\n        if (!m_widget) {\r\n            m_widget = std::make_unique&lt;Widget&gt;();\r\n        }\r\n        return m_widget.get();\r\n    }\r\n};\r\n\r\nvoid Sample()\r\n{\r\n    Doodad doodad;\r\n\r\n    \/\/ Demand-create the widget and increment its counter.\r\n    doodad.GetWidget().Increment();\r\n\r\n    \/\/ This assertion fires?\r\n    ASSERT(doodad.GetWidget().m_count &gt; 0);\r\n}\r\n<\/pre>\n<p>It&#8217;s as if the <code>GetWidget()<\/code> function is creating brand new empty widgets instead of creating one on its first call and reusing it for subsequent calls.<\/p>\n<p style=\"margin-bottom: 30ex;\">Maybe you can spot the problem.<\/p>\n<p>What struck me was that the return type of <code>GetWidget()<\/code> was wrong. The <code>m_widget.get()<\/code> returns a <code>Widget*<\/code>, but the function returns a <code>Widget<\/code> object. But how did this code even compile? The return type is wrong!<\/p>\n<p>The answer is a backward compatibility feature of C++ combined with a poor choice of default in the language design.<\/p>\n<p>The backward compatibility feature is that pointers can implicitly convert to <code>bool<\/code>: The built-in boolean conversion is a comparison against <code>nullptr<\/code>. This is a backward compatibility feature carried over from C.<\/p>\n<p>The poor choice of default in the language is that any constructor that can be called with a single parameter (though possibly with the help of some defaulted parameters) is by default usable as an implicit conversion. To opt out, you must use the <code>explicit<\/code> keyword.<\/p>\n<pre>struct Widget\r\n{\r\n    \/\/ \u2193 usable as implicit conversion from bool\r\n    <span style=\"border: solid 1px currentcolor;\">Widget(bool debugMode = false)<\/span>;\r\n\r\n    int m_count = 0;\r\n    void Increment() { ++m_count; }\r\n};\r\n<\/pre>\n<p>If we write out the implicit conversion, the <code>GetWidget<\/code> becomes<\/p>\n<pre>    Widget GetWidget()\r\n    {\r\n        if (!m_widget) {\r\n            m_widget = std::make_unique&lt;Widget&gt;();\r\n        }\r\n        return <span style=\"border: solid 1px currentcolor;\">Widget(m_widget.get() != nullptr)<\/span>;\r\n    }\r\n<\/pre>\n<p>Now we see more clearly what&#8217;s going on.<\/p>\n<p>The pointer produced by <code>m_widget.get()<\/code> is converted to a <code>bool<\/code> by checking whether it is null. (And since the pointer inside <code>m_widget<\/code> is always non-null by the time we get to the <code>return<\/code> statement, the result is always <code>true<\/code>.) And then we use the <code>Widget<\/code> constructor that takes a single <code>bool<\/code> parameter and use it as a conversion.<\/p>\n<p>The result is that the <code>GetWidget()<\/code> method always returns a freshly-created <code>Widget<\/code> in debug mode.<\/p>\n<p>I was initially baffled as to how the original code compiled, seeing as the return type was wrong. I figured out that it was the implicit conversion by looking at the code generation. The code for the <code>return<\/code> statement looked like this:<\/p>\n<pre>        cmp     QWORD PTR [rbx], 0    ; Q: m_widget.get() == nullptr?\r\n        setne   dl                    ; dl = 1 if non-null (constructor parameter)\r\n        mov     rcx, rdi              ; return value slot\r\n        call    Widget::Widget(bool)  ; create a widget\r\n        mov     rax, rdi              ; and return it\r\n<\/pre>\n<p>One possible fix is to return a pointer to the Widget:<\/p>\n<pre>struct Doodad\r\n{\r\n    \u27e6 ... \u27e7\r\n\r\n    Widget<span style=\"border: solid 1px currentcolor;\">*<\/span> GetWidget()\r\n    {\r\n        if (!m_widget) {\r\n            m_widget = std::make_unique&lt;Widget&gt;();\r\n        }\r\n        return m_widget.get();\r\n    }\r\n};\r\n\r\nvoid Sample()\r\n{\r\n    Doodad doodad;\r\n\r\n    \/\/ Demand-create the widget and increment its counter.\r\n    doodad.GetWidget()<span style=\"border: solid 1px currentcolor;\">-&gt;<\/span>Increment();\r\n\r\n    <span style=\"border: solid 1px currentcolor;\">\/\/ This assertion no longer fires<\/span>\r\n    ASSERT(doodad.GetWidget()<span style=\"border: solid 1px currentcolor;\">-&gt;<\/span>m_count &gt; 0);\r\n}\r\n<\/pre>\n<p>Another option is to return a reference to the Widget.<\/p>\n<pre>struct Doodad\r\n{\r\n    \u27e6 ... \u27e7\r\n\r\n    Widget<span style=\"border: solid 1px currentcolor;\">&amp;<\/span> GetWidget()\r\n    {\r\n        if (!m_widget) {\r\n            m_widget = std::make_unique&lt;Widget&gt;();\r\n        }\r\n        return <span style=\"border: solid 1px currentcolor;\">*m_widget<\/span>;\r\n    }\r\n};\r\n\r\nvoid Sample()\r\n{\r\n    Doodad doodad;\r\n\r\n    \/\/ Demand-create the widget and increment its counter.\r\n    doodad.GetWidget().Increment(); <span style=\"border: solid 1px currentcolor;\">\/\/ no change to callers<\/span>\r\n\r\n    <span style=\"border: solid 1px currentcolor;\">\/\/ This assertion no longer fires<\/span>\r\n    ASSERT(doodad.GetWidget().m_count &gt; 0);\r\n}\r\n<\/pre>\n<p>While we&#8217;re at it, let&#8217;s make that constructor explicit to remove the implicit conversion.<\/p>\n<pre>struct Widget\r\n{\r\n    <span style=\"border: solid 1px currentcolor;\">explicit<\/span> Widget(bool debugMode = false);\r\n\r\n    int m_count = 0;\r\n    void Increment() { ++m_count; }\r\n};\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>C++ language backward compatibility makes legal what you had hoped wasn&#8217;t.<\/p>\n","protected":false},"author":1069,"featured_media":110434,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-111228","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>C++ language backward compatibility makes legal what you had hoped wasn&#8217;t.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111228","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=111228"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111228\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/110434"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=111228"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=111228"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=111228"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}