{"id":108619,"date":"2023-08-18T07:00:00","date_gmt":"2023-08-18T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108619"},"modified":"2023-08-18T06:58:23","modified_gmt":"2023-08-18T13:58:23","slug":"20230818-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230818-00\/?p=108619","title":{"rendered":"Phantom and indulgent shared pointers"},"content":{"rendered":"<p>Last time, we looked at <a title=\"What it means when you convert between different shared_ptrs\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230817-00\/?p=108611\"> various ways to convert among different <code>shared_ptr<\/code>s<\/a>. We finished with this diagram:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse; text-align: center;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>\u00a0<\/th>\n<th>Null control block<\/th>\n<th>Non-null control block<\/th>\n<\/tr>\n<tr>\n<th>Null stored pointer<\/th>\n<td>Empty<\/td>\n<td>Phantom<\/td>\n<\/tr>\n<tr>\n<th>Non-null stored pointer<\/th>\n<td>Indulgent<\/td>\n<td>Full<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>You are familiar with an empty shared pointer, which manages no object and has no stored pointer. You are also familiar with a full shared pointer, which manages an object and has a non-null stored pointer (to the managed object, or something whose lifetime is controlled by the managed object). But what about those other two guys?<\/p>\n<p>In the upper right corner, you have the case of a shared pointer that manages an object but whose stored pointer is null, which I&#8217;ve called a <i>phantom<\/i> shared pointer. If you convert the shared pointer to a <code>bool<\/code>, it produces <code>false<\/code>, because you can&#8217;t use it to access anything. The phantom shared pointer looks empty at a casual glance, but it secretly manages an object behind the scenes. That secretly-managed object remains alive for no visible reason. There is no way to access that secretly-managed object, but it&#8217;s still there. It&#8217;s a phantom which follows you around.<\/p>\n<pre>struct Sample\r\n{\r\n    int value;\r\n};\r\nstd::shared_ptr&lt;Sample&gt; p = std::make_shared&lt;Sample&gt;();\r\nstd::shared_ptr&lt;char&gt; q = std::shared_ptr&lt;char&gt;(p, nullptr);\r\np = nullptr;\r\n<\/pre>\n<p>In the above example, we start by creating a shared pointer to a freshly-constructed <code>Sample<\/code> object. We then use this shared pointer to provide the control block to a new aliasing shared pointer whose stored pointer is <code>nullptr<\/code>. Finally, we clear the original shared pointer, so that the only reference to the <code>Sample<\/code> is in the phantom shared pointer <code>q<\/code>. The <code>Sample<\/code> is now being kept alive by what looks like a null pointer!<\/p>\n<p>In the opposite corner, you have a shared pointer with no control block, but with a non-null stored pointer, which I&#8217;ve called an <i>indulgent<\/i> shared pointer because it points to something, yet owns nothing. &#8220;Go ahead, have fun with this pointer all you like, I don&#8217;t care!&#8221;<\/p>\n<pre>int globalVariable;\r\n\r\nstd::shared_ptr&lt;int&gt; p(std::shared_ptr&lt;int&gt;(), &amp;globalVariable);\r\n<\/pre>\n<p>We use the aliasing constructor for <code>shared_ptr&lt;int&gt;<\/code> which takes another <code>shared_ptr&lt;int&gt;<\/code> (which provides the control block) and a raw pointer (which serves as the stored pointer). The donor <code>shared_ptr<\/code> is empty and has no control block. We give that nonexistent control block to the aliasing constructor, and the result is a shared pointer that owns nothing, yet which points to something, namely, <code>globalVariable<\/code>.<\/p>\n<p>This style of shared pointer is useful if you need a shared pointer that points to memory with static storage duration. The pointer is valid for the duration of the process, so you don&#8217;t need a control block to manage the object&#8217;s lifetime: The object&#8217;s lifetime is &#8220;forever&#8221;.<\/p>\n<p>How can you detect whether you have one of these unusual shared pointers?<\/p>\n<p>If you treat a <code>shared_ptr<\/code> as a <code>bool<\/code>, the resulting value tells you whether the stored pointer is non-null. On the other hand, if you ask <code>use_count()<\/code>, it will return a nonzero value if there is a managed object. Now we can finish our table:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse; text-align: center;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>\u00a0<\/th>\n<th>Null control block<br \/>\n(use_count() == 0)<\/th>\n<th>Non-null control block<br \/>\n(use_count() != 0)<\/th>\n<\/tr>\n<tr>\n<th>Null stored pointer<br \/>\n(falsy)<\/th>\n<td>Empty<\/td>\n<td>Phantom<\/td>\n<\/tr>\n<tr>\n<th>Non-null stored pointer<br \/>\n(truthy)<\/th>\n<td>Indulgent<\/td>\n<td>Full<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Note that phantom shared pointers are falsy, and indulgent shared pointers are truthy. I couldn&#8217;t think of a synonym for indulgent that begins with a &#8220;t&#8221;, sorry.<\/p>\n<p>In code, we can detect the four cases by using <code>use_count()<\/code> and <code>get()<\/code> (or equivalently, converting to <code>bool<\/code>).<\/p>\n<pre>void detect(std::shared_ptr&lt;T&gt; const&amp; p)\r\n{\r\n    bool has_control_block = p.use_count();\r\n    bool has_pointer = p.get();\r\n    \/\/ equivalently, has_pointer = static_cast&lt;bool&gt;(p);\r\n\r\n    if (has_control_block &amp;&amp; has_pointer) {\r\n        \/\/ full\r\n    } else if (has_control_block &amp;&amp; !has_pointer) {\r\n        \/\/ phantom\r\n    } else if (!has_control_block &amp;&amp; has_pointer) {\r\n        \/\/ indulgent\r\n    } else { \/\/ !has_control_block &amp;&amp; !has_pointer)\r\n        \/\/ empty\r\n    }\r\n}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>The phantom controls something yet holds nothing. The indulgent holds something but controls nothing.<\/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-108619","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>The phantom controls something yet holds nothing. The indulgent holds something but controls nothing.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108619","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=108619"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108619\/revisions"}],"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=108619"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108619"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108619"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}