{"id":108891,"date":"2023-10-13T07:00:00","date_gmt":"2023-10-13T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=108891"},"modified":"2023-10-13T08:29:40","modified_gmt":"2023-10-13T15:29:40","slug":"20231013-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20231013-00\/?p=108891","title":{"rendered":"On detecting improper use of <CODE>std::<WBR>enable_<WBR>shared_<WBR>from_<WBR>this<\/CODE>"},"content":{"rendered":"<p>We saw some time ago that <a title=\"My class derives from std::enable_shared_from_this, but shared_from_this() doesn't work\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220720-00\/?p=106877\"> you must publicly derive from <code>std::<wbr \/>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code><\/a> in order for <code>shared_<wbr \/>from_<wbr \/>this()<\/code> to work. Can we fix it so that the problem is detected at compile time rather than failing mysteriously at runtime?<\/p>\n<pre>template&lt;class T&gt;\r\nclass enable_shared_from_this\r\n{\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">static_assert(                                      <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    std::is_convertible_v&lt;T*,                       <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">                          enable_shared_from_this*&gt;,<\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    \"You must publicly derive from \"                <\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">    \"enable_shared_from_this exactly once\");        <\/span>\r\n\r\npublic:\r\n    using esft_tag = enable_shared_from_this;\r\n\r\n    [[nodiscard]] shared_ptr&lt;T&gt; shared_from_this() {\r\n        return shared_ptr&lt;T&gt;(weak);\r\n    }\r\n\r\n    [[nodiscard]] shared_ptr&lt;T&gt; shared_from_this() const {\r\n        return shared_ptr&lt;const T&gt;(weak);\r\n    }\r\n\r\n    [[nodiscard]] weak_ptr&lt;T&gt; weak_from_this() noexcept {\r\n        return weak;\r\n    }\r\n\r\n    [[nodiscard]] weak_ptr&lt;const T&gt; weak_from_this() const noexcept {\r\n        return weak;\r\n    }\r\n\r\nprotected:\r\n    constexpr enable_shared_from_this() noexcept : weak() {}\r\n\r\n    enable_shared_from_this(const enable_shared_from_this&amp;) noexcept {}\r\n    enable_shared_from_this&amp; operator=(const enable_shared_from_this&amp;) noexcept\r\n    { return *this; }\r\n\r\nprivate:\r\n    template&lt;typename U&gt; friend class shared_ptr;\r\n\r\n    mutable weak_ptr&lt;T&gt; weak;\r\n};\r\n<\/pre>\n<p>We assert that the class <code>T<\/code> publicly and uniquely derives from <code>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this&lt;T&gt;<\/code>. Unfortunately, this doesn&#8217;t work because we are asserting too soon:<\/p>\n<pre>class something : std::enable_shared_from_this&lt;something&gt;\r\n\/\/   instantiated ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n{\r\n    \u27e6 ... \u27e7\r\n};\r\n<\/pre>\n<p>At the point that <code>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this&lt;<wbr \/>someting&gt;<\/code> is instantiated, the class <code>something<\/code> is incomplete.<\/p>\n<p>We can defer the assertion to the constructor, because the constructor body is instantiated after the completion of the derived class.<\/p>\n<pre>template&lt;class T&gt;\r\nclass enable_shared_from_this\r\n{\r\npublic:\r\n    using esft_tag = enable_shared_from_this;\r\n\r\n    [[nodiscard]] shared_ptr&lt;T&gt; shared_from_this() {\r\n        return shared_ptr&lt;T&gt;(weak);\r\n    }\r\n\r\n    [[nodiscard]] shared_ptr&lt;T&gt; shared_from_this() const {\r\n        return shared_ptr&lt;const T&gt;(weak);\r\n    }\r\n\r\n    [[nodiscard]] weak_ptr&lt;T&gt; weak_from_this() noexcept {\r\n        return weak;\r\n    }\r\n\r\n    [[nodiscard]] weak_ptr&lt;const T&gt; weak_from_this() const noexcept {\r\n        return weak;\r\n    }\r\n\r\nprotected:\r\n    constexpr enable_shared_from_this() noexcept : weak() {\r\n      <span style=\"border: solid 1px currentcolor; border-bottom: none;\">static_assert(                                      <\/span>\r\n      <span style=\"border: 1px currentcolor; border-style: none solid;\">    std::is_convertible_v&lt;T*,                       <\/span>\r\n      <span style=\"border: 1px currentcolor; border-style: none solid;\">                          enable_shared_from_this*&gt;,<\/span>\r\n      <span style=\"border: 1px currentcolor; border-style: none solid;\">    \"You must publicly derive from \"                <\/span>\r\n      <span style=\"border: solid 1px currentcolor; border-top: none;\">    \"enable_shared_from_this exactly once\");        <\/span>\r\n\r\n    }\r\n\r\n    enable_shared_from_this(const enable_shared_from_this&amp;) noexcept {}\r\n    enable_shared_from_this&amp; operator=(const enable_shared_from_this&amp;) noexcept\r\n    { return *this; }\r\n\r\nprivate:\r\n    template&lt;typename U&gt; friend class shared_ptr;\r\n\r\n    mutable weak_ptr&lt;T&gt; weak;\r\n};\r\n<\/pre>\n<p>But wait, there&#8217;s this other mistake: Deriving from two different specializations of <code>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code>:<\/p>\n<pre>struct B1 : std::enable_shared_from_this&lt;B1&gt; {};\r\nstruct B2 : std::enable_shared_from_this&lt;B2&gt; {};\r\n\r\nstruct D : B1, B2 {};\r\n\r\n\/\/ No complaint!\r\nauto p = std::make_shared&lt;D&gt;();\r\n\r\n\/\/ This throws bad_weak_ptr\r\nauto b1 = p-&gt;B1::shared_from_this();\r\n<\/pre>\n<p>The <code>make_shared&lt;D&gt;()<\/code> executes just fine, but the resulting object&#8217;s <code>std::<wbr \/>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this&lt;B1&gt;<\/code> and <code>std::<wbr \/>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this&lt;B2&gt;<\/code> objects are nonfunctional because <code>D<\/code> derived multiple times from <code>std::<wbr \/>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code>. We failed to detect this in our <code>static_assert<\/code> because the <code>static_assert<\/code> checks only that each individual specialization is unique.<\/p>\n<p>Remember, <code>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code> is not referring to &#8220;all specializations of this template&#8221;. It is referring to the specific specialization being instantiated, since it is the <a title=\"Injected class names: The C++ feature you didn't even realize that you were using\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220321-00\/?p=106367\"> injected class name<\/a>. In other words, the full version of the static assertion is<\/p>\n<pre>static_assert(\r\n    std::is_convertible_v&lt;T*,\r\n                          enable_shared_from_this<span style=\"border: solid 1px currentcolor;\">&lt;T&gt;<\/span>*&gt;,\r\n    \"You must publicly derive from \"\r\n    \"enable_shared_from_this exactly once\");\r\n<\/pre>\n<p>But we want to check <i>all<\/i> specializations, not just the one being instantiated. How can we do that?<\/p>\n<p>We can take advantage of the <code>esft_tag<\/code> that is used by <code>make_<wbr \/>shared()<\/code> to locate the <code>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this()<\/code> base class.<\/p>\n<pre>static_assert(\r\n    std::is_convertible_v&lt;T*,\r\n                          <span style=\"border: solid 1px currentcolor;\">typename T::esft_tag<\/span>*&gt;,\r\n    \"You must publicly derive from \"\r\n    \"enable_shared_from_this exactly once\");\r\n<\/pre>\n<p>If <code>T<\/code> has multiple base classes which are specializations of <code>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this&lt;T&gt;<\/code>, there are two cases:<\/p>\n<ul>\n<li>All of the base classes are the same <code>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this&lt;T&gt;<\/code>. In this case, the conversion from <code>T*<\/code> to <code>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this&lt;T&gt;*<\/code> is ambiguous, and <code>is_convertible_v<\/code> returns <code>false<\/code>.<\/li>\n<li>There are two base classes which are different specializations, say <code>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this&lt;B1&gt;<\/code> and <code>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this&lt;B2&gt;<\/code> (where <code>B1<\/code> \u2260 <code>B2<\/code>). In this case, tthe nested type <code>T::esft_tag<\/code> will have conflicting definitions, and you get an ambiguous type error.<\/li>\n<\/ul>\n<p>The standard <code>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this&lt;T&gt;<\/code> doesn&#8217;t do this extra enforcement because you are allowed to specialize <code>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this&lt;T&gt;<\/code> with an incomplete type!<\/p>\n<pre>struct D;\r\n\r\nstruct B : std::enable_shared_from_this&lt;D&gt;\r\n{\r\n};\r\n\r\nstruct D : B\r\n{\r\n};\r\n\r\nauto p = std::make_shared&lt;D&gt;();\r\nauto q = p-&gt;shared_from_this();\r\n<\/pre>\n<p>We made <code>B<\/code> derive from <code>std::<wbr \/>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this&lt;D&gt;<\/code> even though we don&#8217;t know what <code>D<\/code> is yet. This is legal. It just means that if you ever decide to put <code>B<\/code> inside a <code>shared_ptr<\/code>, it had better be a base class of a larger <code>D<\/code> object.<\/p>\n<p>So let&#8217;s just declare that case as out of scope for our &#8220;strict <code>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code>&#8220;.<\/p>\n<p>Can we augment the standard <code>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code> to add this sort of safety checking? Let&#8217;s try it:<\/p>\n<pre>template&lt;typename T&gt;\r\nstruct strict_enable_shared_from_this\r\n    : std::enable_shared_from_this&lt;T&gt;\r\n{\r\n    using esft_tag = std::enable_shared_from_this&lt;T&gt;;\r\n};\r\n\r\ntemplate&lt;typename T, typename... Args&gt;\r\nstd::shared_ptr&lt;T&gt;\r\nstrict_make_shared(Args&amp;&amp;... args)\r\n{\r\n    static_assert(\r\n        std::is_convertible_v&lt;T*,\r\n                              typename T::esft_tag*&gt;,\r\n        \"You must publicly derive from \"\r\n        \"strict_enable_shared_from_this exactly once\");\r\n    return std::make_shared&lt;T&gt;(std::forward&lt;Args&gt;(args)...);\r\n}\r\n<\/pre>\n<p>If we can be sure that everybody uses <code>strict_<wbr \/>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code>, then <code>strict_<wbr \/>make_<wbr \/>shared<\/code> can verify that the resulting <code>shared_ptr<\/code> owns an object which indeed holds a weak pointer to itself. On the other hand, if somebody sneaks in and uses a standard <code>enable_<wbr \/>shared_<wbr \/>from_<wbr \/>this<\/code>, then they can avoid the detection.<\/p>\n<pre>struct B1 : strict_enable_shared_from_this&lt;D&gt; {};\r\nstruct B2 : std::enable_shared_from_this&lt;D&gt; {};\r\nstruct D: B1, B2 {};\r\n\r\n\/\/ No complaint, but the shared_from_this method fails.\r\nauto p = strict_make_shared&lt;D&gt;();\r\n<\/pre>\n<p>It would be nice if the C++ standard library had a type trait<\/p>\n<pre>template&lt;typename T&gt;\r\nstruct can_enable_shared_from_this;\r\n\r\ntemplate&lt;typename T&gt;\r\ninline constexpr bool can_enable_shared_from_this_v =\r\n    can_enable_shared_from_this&lt;T&gt;::value;\r\n<\/pre>\n<p>Then we can we can define our own (or maybe the C++ standard libray can also provide)<\/p>\n<pre>template&lt;typename T, typename... Args&gt;\r\nstd::shared_ptr&lt;T&gt;\r\nmake_shared_with_weak_ptr(Args&amp;&amp;... args)\r\n{\r\n    static_assert(\r\n        std::can_enable_shared_from_this_v&lt;T&gt;);\r\n    return std::make_shared&lt;T&gt;(std::forward&lt;Args&gt;(args)...);\r\n}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Playing around with the standard library.<\/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-108891","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Playing around with the standard library.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108891","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=108891"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/108891\/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=108891"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=108891"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=108891"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}