{"id":111488,"date":"2025-08-19T07:00:00","date_gmt":"2025-08-19T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=111488"},"modified":"2025-08-15T16:08:14","modified_gmt":"2025-08-15T23:08:14","slug":"20250819-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250819-00\/?p=111488","title":{"rendered":"Thoughts on creating a tracking pointer class, part 7: Non-modifying trackers, second try"},"content":{"rendered":"<p>Last time, we tried to <a title=\"Thoughts on creating a tracking pointer class, part 6: Non-modifying trackers\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250818-00\/?p=111486\"> add non-modifying trackers to our tracking pointers implementation<\/a>. I noted at the end that our attempt was wrong.<\/p>\n<p>The problem is in the code we didn&#8217;t change:<\/p>\n<pre>    void set_target(T* p) noexcept\r\n    {\r\n        for (tracking_node* n = m_trackers.next;\r\n             n != &amp;m_trackers; n = n-&gt;next) {\r\n            <span style=\"border: solid 1px currentcolor;\">static_cast&lt;tracking_ptr&lt;T&gt;*&gt;(n)<\/span>-&gt;tracked = p;\r\n        }\r\n    }\r\n<\/pre>\n<p>The <code>static_cast<\/code> is a downcast from a <code>tracking_node<\/code> to its derived <code>tracking_ptr&lt;T&gt;<\/code>. But the derived class might not be <code>tracking_ptr&lt;T&gt;<\/code>! It could be a <code>tracking_ptr&lt;const T&gt;<\/code>.<\/p>\n<p>To fix this, we need to use a consistent type for the derived class. We can do this by renaming our original <code>tracking_ptr<\/code> to <code>tracking_ptr_base<\/code>, which will serve as the consistent derived class, and then move the <code>get()<\/code> method to a <code>tracking_ptr<\/code> that is derived from <code>tracking_ptr_base<\/code>.<\/p>\n<pre>template&lt;typename T&gt;\r\nstruct <span style=\"border: solid 1px currentcolor;\">tracking_ptr_base<\/span> : private tracking_node\r\n{\r\n    \/\/ <span style=\"text-decoration: line-through;\">T* get() const { return tracked; }<\/span>\r\n\r\n    <span style=\"border: solid 1px currentcolor;\">tracking_ptr_base<\/span>() noexcept :\r\n        tracking_node(as_solo{}),\r\n        tracked(nullptr) {}\r\n\r\n    <span style=\"border: solid 1px currentcolor;\">tracking_ptr_base<\/span>(<span style=\"border: solid 1px currentcolor;\">tracking_ptr_base<\/span> const&amp; other) noexcept :\r\n        tracking_node(copy_node(other)),\r\n        tracked(other.tracked) { }\r\n\r\n    ~<span style=\"border: solid 1px currentcolor;\">tracking_ptr_base<\/span>() = default;\r\n\r\n    <span style=\"border: solid 1px currentcolor;\">tracking_ptr_base<\/span>&amp; operator=(<span style=\"border: solid 1px currentcolor;\">tracking_ptr_base<\/span> const&amp; other) noexcept {\r\n        tracked = other.tracked;\r\n        if (tracked) {\r\n            join(trackers(tracked));\r\n        } else {\r\n            disconnect();\r\n        }\r\n        return *this;\r\n    }\r\n\r\n    <span style=\"border: solid 1px currentcolor;\">tracking_ptr_base<\/span>&amp; operator=(<span style=\"border: solid 1px currentcolor;\">tracking_ptr_base<\/span>&amp;&amp; other) noexcept {\r\n        tracked = std::exchange(other.tracked, nullptr);\r\n        tracking_node::displace(other);\r\n        return *this;\r\n    }\r\n\r\nprivate:\r\n    friend struct trackable_object&lt;T&gt;;\r\n\r\n    static tracking_node&amp; trackers(T* p) noexcept {\r\n        return p-&gt;trackable_object&lt;T&gt;::m_trackers;\r\n    }\r\n\r\n    tracking_node copy_node(<span style=\"border: solid 1px currentcolor;\">tracking_ptr_base<\/span> const&amp; other) noexcept\r\n    {\r\n        if (other.tracked) {\r\n            return tracking_node(as_join{},\r\n                                 trackers(other.tracked));\r\n        } else {\r\n            return tracking_node(as_solo{});\r\n        }\r\n    }\r\n\r\n    <span style=\"border: solid 1px currentcolor;\">tracking_ptr_base<\/span>(T* p) noexcept :\r\n        tracking_node(as_join{}, trackers(p)),\r\n        tracked(p) { }\r\n\r\n<span style=\"border: solid 1px currentcolor;\">protected:<\/span>\r\n    T* tracked;\r\n};\r\n\r\n<span style=\"border: solid 1px currentcolor; border-bottom: none;\">template&lt;typename T&gt;                                        <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">struct tracking_ptr : tracking_ptr_base&lt;std::remove_cv_t&lt;T&gt;&gt;<\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">{                                                           <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">public:                                                     <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">    T* get() const { return this-&gt;tracked; }                <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">\u00a0                                                           <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">    using <a title=\"Why can't I find the injected name of a templated class's templated base class?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240517-00\/?p=109774\">tracking_ptr<\/a>::tracking_ptr_base::                 <\/span>\r\n<span style=\"border: 1px currentcolor; border-style: none solid;\">                        tracking_ptr_base;                  <\/span>\r\n<span style=\"border: solid 1px currentcolor; border-top: none;\">};                                                          <\/span>\r\n\r\ntemplate&lt;typename T&gt;\r\nstruct trackable_object\r\n{\r\n    \u27e6 ... \u27e7\r\n\r\nprivate:\r\n    friend struct <span style=\"border: solid 1px currentcolor;\">tracking_ptr_base<\/span>&lt;T&gt;;\r\n    \/\/ <span style=\"text-decoration: line-through;\">friend struct tracking_ptr&lt;const T&gt;;<\/span>\r\n\r\n    \u27e6 ... \u27e7\r\n\r\n    void set_target(T* p)\r\n    {\r\n        for (tracking_node* n = m_trackers.next;\r\n            n != &amp;m_trackers; n = n-&gt;next) {\r\n            static_cast&lt;<span style=\"border: solid 1px currentcolor;\">tracking_ptr_base<\/span>&lt;T&gt;*&gt;(n)-&gt;\r\n                tracked = p;\r\n        }\r\n    }\r\n};\r\n<\/pre>\n<p>Okay, now we can have an object give away a non-modifying tracking pointer to itself by using <code>ctrack()<\/code> instead of <code>track()<\/code>.<\/p>\n<p>But wait, this still requires that the original object be itself mutable. But if all you have is a const reference to a trackable object, surely you should be allowed to create a non-modifying tracking pointer to it, right?<\/p>\n<p>We&#8217;ll do that next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Another attempt to make tracking pointers to objects that you can&#8217;t modify.<\/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-111488","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Another attempt to make tracking pointers to objects that you can&#8217;t modify.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111488","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=111488"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111488\/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=111488"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=111488"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=111488"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}