{"id":108608,"date":"2023-08-16T07:00:00","date_gmt":"2023-08-16T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108608"},"modified":"2024-11-17T11:34:11","modified_gmt":"2024-11-17T19:34:11","slug":"20230816-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230816-00\/?p=108608","title":{"rendered":"Inside STL: The <CODE>shared_ptr<\/CODE> constructor and <CODE>enable_shared_from_this<\/CODE>"},"content":{"rendered":"<p>If you create a class of the form<\/p>\n<pre>struct S : std::enable_shared_from_this&lt;S&gt;\r\n{\r\n    \/* ... *\/\r\n};\r\n<\/pre>\n<p>which derives from <code>std::<wbr \/>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code> of itself (using the curiously recurring template pattern), then this class becomes a candidate for special treatment by <code>shared_ptr<\/code>: The <code>shared_<wbr \/>from_<wbr \/>this()<\/code> method will produce a <code>shared_<wbr \/>ptr&lt;S&gt;<\/code>. Some restrictions apply.<\/p>\n<p>Here&#8217;s how it works.<\/p>\n<pre>template&lt;typename T&gt;\r\nstruct enable_shared_from_this\r\n{\r\n    using esft_detector = enable_shared_from_this;\r\n    std::weak_ptr&lt;T&gt; weak_this;\r\n\r\n    std::weak_ptr&lt;T&gt; weak_from_this()\r\n    { return weak_this; }\r\n\r\n    std::shared_ptr&lt;T&gt; shared_from_this()\r\n    { return weak_this.lock(); }\r\n\r\n};\r\n<\/pre>\n<p>When you derive from <code>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code>, you get a secret weak pointer which the C++ standard calls <code>weak_this<\/code>. The inherited member function <code>weak_<wbr \/>from_<wbr \/>this()<\/code> returns that weak pointer, and the inherited member function <code>shared_<wbr \/>from_<wbr \/>this()<\/code> returns a shared version of that weak pointer.<\/p>\n<p>Who initializes this weak pointer?<\/p>\n<p>When the control block is created, the <code>shared_ptr&lt;S&gt;<\/code> constructor snoops at the object that is being managed by the control block. If it uniquely inherits from <code>std::<wbr \/>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code> and does so publicly, then the constructor stashes a weak pointer to the newly-constructed <code>shared_ptr<\/code> in <code>weak_this<\/code>.<\/p>\n<p>That&#8217;s the only time it happens. If anything goes wrong, you don&#8217;t get your <code>weak_this<\/code>, and the <code>weak_<wbr \/>from_<wbr \/>this()<\/code> and <code>shared_<wbr \/>from_<wbr \/>this()<\/code> methods throw a &#8220;bad weak reference&#8221; exception.<\/p>\n<p>Here are some things that could go wrong:<\/p>\n<ul>\n<li>The <code>S<\/code> object was never created as part of a <code>shared_ptr<\/code>. Maybe it was created as a local variable or as a member of a larger structure.<\/li>\n<li>The <code>S<\/code> object derives from <code>std::<wbr \/>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code>, but the base class was not <i>public<\/i>.<\/li>\n<li>The <code>S<\/code> object derives from <code>std::<wbr \/>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code> <i>more than once<\/i>.<\/li>\n<\/ul>\n<p>Some time ago, I discussed <a title=\"Making sure that people use make_unique and make_shared to make your object\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220721-00\/?p=106879\"> a way to make sure people use <code>make_shared<\/code> to make the object<\/a>, which you can use to reduce the likelihood of the first problem.<\/p>\n<p>The second problem is often an oversight, forgetting that base classes of a <code>class<\/code> are private by default. (Base classes of a <code>struct<\/code> are public by default.)<\/p>\n<p>The third problem is a more complex oversight which usually comes about when you build a derivation hierarchy out of multiple pieces, unaware that some of the pieces are already using <code>std::<wbr \/>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code>.<\/p>\n<p>Okay, so that&#8217;s what it does, but how does it work?<\/p>\n<p>The <code>shared_ptr<\/code> constructor detects the presence of a unique <code>std::<wbr \/>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code> base class by using the <code>esft_detector<\/code> that I put in the expository declaration.<\/p>\n<pre>template&lt;typename T, typename = void&gt;\r\nstruct supports_esft : std::false_type {};\r\n\r\ntemplate&lt;typename T&gt;\r\nstruct inline bool supports_esft&lt;T,\r\n    std::void_t&lt;typename T::esft_detector&gt;&gt;\r\n    : std::true_type {};\r\n<\/pre>\n<p>Our first attempt at detecting <code>std::<wbr \/>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code> support is checking whether our marker type <code>esft_<wbr \/>detector<\/code> is available. If there is no <code>std::<wbr \/>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code> in the derivation hierarchy, then the type will be missing outright. If it is present but not <code>public<\/code>, then the check will fail due to the type being inaccessible.<\/p>\n<p>The code that sets the weak pointer uses this detector helper:<\/p>\n<pre>template&lt;typename T, typename D&gt;\r\nstruct shared_ptr\r\n{\r\n    shared_ptr(T* ptr)\r\n    {\r\n        ... do the usual stuff ...\r\n\r\n        \/* Here comes enable_shared_from_this magic *\/\r\n        if constexpr (supports_esft&lt;T&gt;::value) {\r\n            using detector = T::esft_detector;\r\n            ptr-&gt;detector::weak_this = *this;\r\n        }\r\n    }\r\n\r\n    ... other constructors and stuff ...\r\n};\r\n<\/pre>\n<p>If the <code>esft_detector<\/code> is present, then we use it to tell us which specialization of <code>std::<wbr \/>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code> was used, so that we can set that base class&#8217;s <code>weak_this<\/code>.<\/p>\n<p>We can&#8217;t stop here, though, because this results in a compilation error if there are multiple <code>std::<wbr \/>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code> base classes.<\/p>\n<pre>struct B : std::enable_shared_from_this&lt;B&gt; {};\r\nstruct M1 : B {};\r\nstruct M2 : B {};\r\nstruct D : M1, M2 {};\r\n\r\nauto p = std::make_shared&lt;D&gt;();\r\n\r\nerror: ambiguous reference to base class at\r\n\r\nptr-&gt;detector::weak_this = *this;\r\n     ^^^^^^^^\r\n<\/pre>\n<p>To avoid this, we also ensure that the detector is <i>unique<\/i>.<\/p>\n<pre>template&lt;typename T&gt;\r\nstruct inline bool supports_esft&lt;T,\r\n    std::void_t&lt;typename T::esft_detector&gt;&gt;\r\n    : std::is_convertible&lt;T *, typename T::esft_detector *&gt;::type {};\r\n<\/pre>\n<p>If a pointer to <code>T<\/code> is convertible to a pointer to the detector, then we know that the detector appears only once among the base classes of <code>T<\/code>.<\/p>\n<p>One could argue that instead of silently ignoring the cases where <code>std::<wbr \/>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code> was declared but could not be used, the language could have said that such a program is ill-formed and produces a compiler error. But no, the language says that if you break rules 2 or 3, then the <code>std::<wbr \/>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code> is simply ignored, and you are left scratching your head trying to figure out where you went astray.<\/p>\n<p>I suspect part of the problem is that it is explicitly legal to use <code>shared_ptr&lt;T&gt;<\/code> when <code>T<\/code> is an incomplete type.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Working together through a secret signal.<\/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-108608","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Working together through a secret signal.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108608","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=108608"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108608\/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=108608"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108608"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108608"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}